/*
 * Tests for WIDL and RPC server/clients.
 *
 * Copyright (C) Google 2007 (Dan Hipschman)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#define COBJMACROS
#include <windows.h>
#include <ole2.h>
#include <oleauto.h>
#include <secext.h>
#include <rpcdce.h>
#include <netfw.h>
#include "wine/test.h"
#include "server.h"
#define SKIP_TYPE_DECLS
#include "server_interp.h"
#include "server_defines.h"
#include "explicit_handle.h"

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

#define PORT "4114"
#define PIPE "\\pipe\\wine_rpcrt4_test"

#define INT_CODE 4198

static const char *progname, *client_test_name;
static BOOL old_windows_version;

static HANDLE stop_event, stop_wait_event;
static PROCESS_INFORMATION client_info;

static void (WINAPI *pNDRSContextMarshall2)(RPC_BINDING_HANDLE, NDR_SCONTEXT, void*, NDR_RUNDOWN, void*, ULONG);
static NDR_SCONTEXT (WINAPI *pNDRSContextUnmarshall2)(RPC_BINDING_HANDLE, void*, ULONG, void*, ULONG);
static RPC_STATUS (WINAPI *pRpcServerRegisterIfEx)(RPC_IF_HANDLE,UUID*, RPC_MGR_EPV*, unsigned int,
                   unsigned int,RPC_IF_CALLBACK_FN*);
static RPC_STATUS (WINAPI *pRpcBindingSetAuthInfoExA)(RPC_BINDING_HANDLE, RPC_CSTR, ULONG, ULONG,
                                                      RPC_AUTH_IDENTITY_HANDLE, ULONG, RPC_SECURITY_QOS *);
static RPC_STATUS (WINAPI *pRpcServerRegisterAuthInfoA)(RPC_CSTR, ULONG, RPC_AUTH_KEY_RETRIEVAL_FN, LPVOID);

static char *domain_and_user;

static int (__cdecl *int_return)(void);
static int (__cdecl *square)(int x);
static int (__cdecl *sum)(int x, int y);
static signed char (__cdecl *sum_char)(signed char x, signed char y);
static short (__cdecl *sum_short)(short x, short y);
static int (__cdecl *sum_float)(float x, float y);
static int (__cdecl *sum_double_int)(int x, double y);
static hyper (__cdecl *sum_hyper)(hyper x, hyper y);
static int (__cdecl *sum_hyper_int)(hyper x, hyper y);
static int (__cdecl *sum_char_hyper)(signed char x, hyper y);
static void (__cdecl *square_out)(int x, int *y);
static int (__cdecl *sum_chars)(int a, chars_t x, chars_t y);
static int (__cdecl *sum_ints)(int a, ints_t x, ints_t y);
static int (__cdecl *sum_flts)(int a, flts_t x, flts_t y);
static int (__cdecl *sum_dbls)(int a, dbls_t x, dbls_t y);
static int (__cdecl *sum_iiff)(int x0, int y0, float x1, float y1);
static int (__cdecl *sum_ifif)(int x0, float y0, int x1, float y1);
static int (__cdecl *sum_iidd)(int x0, int y0, double x1, double y1);
static int (__cdecl *sum_idid)(int x0, double y0, int x1, double y1);
static int (__cdecl *sum_ififififififif)(int x0, float y0, int x1, float y1, int x2, float y2, int x3, float y3, int x4, float y4, int x5, float y5, int x6, float y6);
static int (__cdecl *sum_ididididididid)(int x0, double y0, int x1, double y1, int x2, double y2, int x3, double y3, int x4, double y4, int x5, double y5, int x6, double y6);
static int (__cdecl *sum_idfdifdfifdfidifdidf)(int x0, double y0, float z0, double y1, int x1, float z1, double y2, float z2, int x2, float z3, double y3, float z4, int x3, double y4, int x4, float z5, double y5, int x5, double y6, float z6);
static int (__cdecl *sum_ididididididididididid)(int x0, double y0, int x1, double y1, int x2, double y2, int x3, double y3, int x4, double y4, int x5, double y5, int x6, double y6, int x7, double y7, int x8, double y8);
static int (__cdecl *sum_iidiidiidiidiidiidiidiidiidiidiid)(int x0, int x1, double y0, int x2, int x3, double y1, int x4, int x5, double y2, int x6, int x7, double y3, int x8, int x9, double y4, int x10, int x11, double y5, int x12, int x13, double y6, int x14, int x15, double y7, int x16, int x17, double y8);
static int (__cdecl *sum_iyiy)(int x0, hyper y0, int x1, hyper y1);
static void (__cdecl *square_ref)(int *x);
static int (__cdecl *str_length)(const char *s);
static int (__cdecl *str_t_length)(str_t s);
static int (__cdecl *cstr_length)(const char *s, int n);
static int (__cdecl *dot_self)(vector_t *v);
static double (__cdecl *square_half)(double x, double *y);
static float (__cdecl *square_half_float)(float x, float *y);
static LONG (__cdecl *square_half_long)(LONG x, LONG *y);
static int (__cdecl *sum_fixed_array)(int a[5]);
static int (__cdecl *pints_sum)(pints_t *pints);
static double (__cdecl *ptypes_sum)(ptypes_t *ptypes);
static int (__cdecl *dot_pvectors)(pvectors_t *pvectors);
static int (__cdecl *sum_sp)(sp_t *sp);
static double (__cdecl *square_sun)(sun_t *su);
static int (__cdecl *test_list_length)(test_list_t *ls);
static int (__cdecl *sum_fixed_int_3d)(int m[2][3][4]);
static int (__cdecl *sum_conf_array)(int x[], int n);
static int (__cdecl *sum_conf_ptr_by_conf_ptr)(int n1, int *n2_then_x1, int *x2);
static int (__cdecl *sum_unique_conf_array)(int x[], int n);
static int (__cdecl *sum_unique_conf_ptr)(int *x, int n);
static int (__cdecl *sum_var_array)(int x[20], int n);
static int (__cdecl *dot_two_vectors)(vector_t vs[2]);
static void (__cdecl *get_number_array)(int x[20], int *n);
static int (__cdecl *sum_cs)(cs_t *cs);
static int (__cdecl *sum_cps)(cps_t *cps);
static int (__cdecl *sum_cpsc)(cpsc_t *cpsc);
static int (__cdecl *get_cpsc)(int n, cpsc_t *cpsc);
static int (__cdecl *sum_complex_array)(int n, refpint_t pi[]);
static int (__cdecl *sum_blob)(cs_blob_t *blob);
static int (__cdecl *sum_data)(cs_data_t *data);
static int (__cdecl *sum_container)(cs_container_t *container);
static int (__cdecl *square_puint)(puint_t p);
static int (__cdecl *sum_puints)(puints_t *p);
static int (__cdecl *sum_cpuints)(cpuints_t *p);
static int (__cdecl *dot_copy_vectors)(vector_t u, vector_t v);
static double (__cdecl *square_encu)(encu_t *eu);
static double (__cdecl *square_unencu)(int t, unencu_t *eu);
static int (__cdecl *sum_parr)(int *a[3]);
static int (__cdecl *sum_pcarr)(int *a[], int n);
static int (__cdecl *enum_ord)(e_t e);
static double (__cdecl *square_encue)(encue_t *eue);
static void (__cdecl *check_se2)(se_t *s);
static int (__cdecl *sum_toplev_conf_2n)(int *x, int n);
static int (__cdecl *sum_toplev_conf_cond)(int *x, int a, int b, int c);
static int (__cdecl *square_test_us)(test_us_t *tus);
static double (__cdecl *sum_aligns)(aligns_t *a);
static int (__cdecl *sum_padded)(padded_t *p);
static int (__cdecl *sum_padded2)(padded_t ps[2]);
static int (__cdecl *sum_padded_conf)(padded_t *ps, int n);
static int (__cdecl *sum_bogus)(bogus_t *b);
static void (__cdecl *check_null)(int *null);
static int (__cdecl *str_struct_len)(str_struct_t *s);
static int (__cdecl *wstr_struct_len)(wstr_struct_t *s);
static int (__cdecl *sum_doub_carr)(doub_carr_t *dc);
static void (__cdecl *make_pyramid_doub_carr)(unsigned char n, doub_carr_t **dc);
static unsigned (__cdecl *hash_bstr)(bstr_t s);
static void (__cdecl *get_a_bstr)(bstr_t *s);
static void (__cdecl *get_name)(name_t *name);
static void (__cdecl *get_names)(int *n, str_array_t *names);
static void (__cdecl *get_namesw)(int *n, wstr_array_t *names);
static int (__cdecl *sum_pcarr2)(int n, int **pa);
static int (__cdecl *sum_L1_norms)(int n, vector_t *vs);
static s123_t* (__cdecl *get_s123)(void);
static void (__cdecl *get_numbers)(int length, int size, pints_t pn[]);
static void (__cdecl *get_numbers_struct)(numbers_struct_t **ns);
static str_t (__cdecl *get_filename)(void);
static rint_t (__cdecl *echo_ranged_int)(int i, int j, int k);
static rint_t (__cdecl *echo_ranged_int2)(int i);
static void (__cdecl *get_ranged_enum)(renum_t *re);
static void (__cdecl *context_handle_test)(void);
static void (__cdecl *full_pointer_test)(int *a, int *b);
static void (__cdecl *full_pointer_null_test)(int *a, int *b);
static void (__cdecl *authinfo_test)(unsigned int protseq, int secure);
static void (__cdecl *stop)(void);
static void (__cdecl *stop_autolisten)(void);
static void (__cdecl *ip_test)(ipu_t *a);
static int (__cdecl *sum_ptr_array)(int *a[2]);
static int (__cdecl *sum_array_ptr)(int (*a)[2]);
static ctx_handle_t (__cdecl *get_handle)(void);
static void (__cdecl *get_handle_by_ptr)(ctx_handle_t *r);
static void (__cdecl *test_handle)(ctx_handle_t ctx_handle);
static void (__cdecl *test_I_RpcBindingInqLocalClientPID)(unsigned int protseq, RPC_BINDING_HANDLE binding);

#define SERVER_FUNCTIONS \
    X(int_return) \
    X(square) \
    X(sum) \
    X(sum_char) \
    X(sum_short) \
    X(sum_float) \
    X(sum_double_int) \
    X(sum_hyper) \
    X(sum_hyper_int) \
    X(sum_char_hyper) \
    X(sum_chars) \
    X(sum_ints) \
    X(sum_flts) \
    X(sum_dbls) \
    X(sum_iiff) \
    X(sum_ifif) \
    X(sum_iidd) \
    X(sum_idid) \
    X(sum_ififififififif) \
    X(sum_ididididididid) \
    X(sum_idfdifdfifdfidifdidf) \
    X(sum_ididididididididididid) \
    X(sum_iidiidiidiidiidiidiidiidiidiidiid) \
    X(sum_iyiy) \
    X(square_out) \
    X(square_ref) \
    X(str_length) \
    X(str_t_length) \
    X(cstr_length) \
    X(dot_self) \
    X(square_half) \
    X(square_half_float) \
    X(square_half_long) \
    X(sum_fixed_array) \
    X(pints_sum) \
    X(ptypes_sum) \
    X(dot_pvectors) \
    X(sum_sp) \
    X(square_sun) \
    X(test_list_length) \
    X(sum_fixed_int_3d) \
    X(sum_conf_array) \
    X(sum_conf_ptr_by_conf_ptr) \
    X(sum_unique_conf_array) \
    X(sum_unique_conf_ptr) \
    X(sum_var_array) \
    X(dot_two_vectors) \
    X(get_number_array) \
    X(sum_cs) \
    X(sum_cps) \
    X(sum_cpsc) \
    X(get_cpsc) \
    X(sum_complex_array) \
    X(sum_blob) \
    X(sum_data) \
    X(sum_container) \
    X(square_puint) \
    X(sum_puints) \
    X(sum_cpuints) \
    X(dot_copy_vectors) \
    X(square_encu) \
    X(square_unencu) \
    X(sum_parr) \
    X(sum_pcarr) \
    X(enum_ord) \
    X(square_encue) \
    X(check_se2) \
    X(sum_toplev_conf_2n) \
    X(sum_toplev_conf_cond) \
    X(square_test_us) \
    X(sum_aligns) \
    X(sum_padded) \
    X(sum_padded2) \
    X(sum_padded_conf) \
    X(sum_bogus) \
    X(check_null) \
    X(str_struct_len) \
    X(wstr_struct_len) \
    X(sum_doub_carr) \
    X(make_pyramid_doub_carr) \
    X(hash_bstr) \
    X(get_a_bstr) \
    X(get_name) \
    X(get_names) \
    X(get_namesw) \
    X(sum_pcarr2) \
    X(sum_L1_norms) \
    X(get_s123) \
    X(get_numbers) \
    X(get_numbers_struct) \
    X(get_filename) \
    X(echo_ranged_int) \
    X(echo_ranged_int2) \
    X(get_ranged_enum) \
    X(context_handle_test) \
    X(full_pointer_test) \
    X(full_pointer_null_test) \
    X(authinfo_test) \
    X(stop) \
    X(stop_autolisten) \
    X(ip_test) \
    X(sum_ptr_array) \
    X(sum_array_ptr) \
    X(get_handle) \
    X(get_handle_by_ptr) \
    X(test_handle) \
    X(test_I_RpcBindingInqLocalClientPID)

/* type check statements generated in header file */
fnprintf *p_printf = printf;

static void set_interp_interface(void)
{
#define X(name) name = interp_##name;
    SERVER_FUNCTIONS
#undef X
}

static void set_mixed_interface(void)
{
#define X(name) name = mixed_##name;
    SERVER_FUNCTIONS
#undef X
}

static void InitFunctionPointers(void)
{
    HMODULE hrpcrt4 = GetModuleHandleA("rpcrt4.dll");

    pNDRSContextMarshall2 = (void *)GetProcAddress(hrpcrt4, "NDRSContextMarshall2");
    pNDRSContextUnmarshall2 = (void *)GetProcAddress(hrpcrt4, "NDRSContextUnmarshall2");
    pRpcServerRegisterIfEx = (void *)GetProcAddress(hrpcrt4, "RpcServerRegisterIfEx");
    pRpcBindingSetAuthInfoExA = (void *)GetProcAddress(hrpcrt4, "RpcBindingSetAuthInfoExA");
    pRpcServerRegisterAuthInfoA = (void *)GetProcAddress(hrpcrt4, "RpcServerRegisterAuthInfoA");

    if (!pNDRSContextMarshall2) old_windows_version = TRUE;
}

void __RPC_FAR *__RPC_USER
midl_user_allocate(SIZE_T n)
{
  return malloc(n);
}

void __RPC_USER
midl_user_free(void __RPC_FAR *p)
{
  free(p);
}

int __cdecl s_int_return(void)
{
  return INT_CODE;
}

int __cdecl s_square(int x)
{
  return x * x;
}

int __cdecl s_sum(int x, int y)
{
  return x + y;
}

signed char __cdecl s_sum_char(signed char x, signed char y)
{
    return x + y;
}

short __cdecl s_sum_short(short x, short y)
{
    return x + y;
}

int __cdecl s_sum_float(float x, float y)
{
    return x + y;
}

int __cdecl s_sum_double_int(int x, double y)
{
    return x + y;
}

hyper __cdecl s_sum_hyper(hyper x, hyper y)
{
    return x + y;
}

int __cdecl s_sum_hyper_int(hyper x, hyper y)
{
    return x + y;
}

int __cdecl s_sum_char_hyper(signed char x, hyper y)
{
    return x + y;
}

int __cdecl s_sum_chars(int a, chars_t x, chars_t y)
{
    return a * (x.a + x.b + x.c + x.d + x.e + y.a + y.b + y.c + y.d + y.e);
}

int __cdecl s_sum_ints(int a, ints_t x, ints_t y)
{
    return a * (x.i + x.j + y.i + y.j);
}

int __cdecl s_sum_flts(int a, flts_t x, flts_t y)
{
    return a * (x.i + x.f + y.i + y.f);
}

int __cdecl s_sum_dbls(int a, dbls_t x, dbls_t y)
{
    return a * (x.i + x.d + y.i + y.d);
}

int __cdecl s_sum_iiff(int x0, int y0, float x1, float y1)
{
    return x0 + y0 + x1 + y1;
}

int __cdecl s_sum_ifif(int x0, float y0, int x1, float y1)
{
    return x0 + y0 + x1 + y1;
}

int __cdecl s_sum_iidd(int x0, int y0, double x1, double y1)
{
    return x0 + y0 + x1 + y1;
}

int __cdecl s_sum_idid(int x0, double y0, int x1, double y1)
{
    return x0 + y0 + x1 + y1;
}

int __cdecl s_sum_ififififififif(int x0, float y0, int x1, float y1, int x2, float y2, int x3, float y3, int x4, float y4, int x5, float y5, int x6, float y6)
{
    return x0 + y0 + x1 + y1 + x2 + y2 + x3 + y3 + x4 + y4 + x5 + y5 + x6 + y6;
}

int __cdecl s_sum_ididididididid(int x0, double y0, int x1, double y1, int x2, double y2, int x3, double y3, int x4, double y4, int x5, double y5, int x6, double y6)
{
    return x0 + y0 + x1 + y1 + x2 + y2 + x3 + y3 + x4 + y4 + x5 + y5 + x6 + y6;
}

int __cdecl s_sum_idfdifdfifdfidifdidf(int x0, double y0, float z0, double y1, int x1, float z1, double y2, float z2, int x2, float z3, double y3, float z4, int x3, double y4, int x4, float z5, double y5, int x5, double y6, float z6)
{
    return x0 + y0 + z0 + x1 + y1 + z1 + x2 + y2 + z2 + x3 + y3 + z3 + x4 + y4 + z4 + x5 + y5 + z5 + y6 + z6;
}

int __cdecl s_sum_ididididididididididid(int x0, double y0, int x1, double y1, int x2, double y2, int x3, double y3, int x4, double y4, int x5, double y5, int x6, double y6, int x7, double y7, int x8, double y8)
{
    return x0 + y0 + x1 + y1 + x2 + y2 + x3 + y3 + x4 + y4 + x5 + y5 + x6 + y6 + x7 + y7 + x8 + y8;
}

int __cdecl s_sum_iidiidiidiidiidiidiidiidiidiidiid(int x0, int x1, double y0, int x2, int x3, double y1, int x4, int x5, double y2, int x6, int x7, double y3, int x8, int x9, double y4, int x10, int x11, double y5, int x12, int x13, double y6, int x14, int x15, double y7, int x16, int x17, double y8)
{
    return x0 + x1 + y0 + x2 + x3 + y1 + x4 + x5 + y2 + x6 + x7 + y3 + x8 + x9 + y4 + x10 + x11 + y5 + x12 + x13 + y6 + x14 + x15 + y7 + x16 + x17 + y8;
}

int __cdecl s_sum_iyiy(int x0, hyper y0, int x1, hyper y1)
{
    return x0 + y0 + x1 + y1;
}

void __cdecl s_square_out(int x, int *y)
{
  *y = s_square(x);
}

void __cdecl s_square_ref(int *x)
{
  *x = s_square(*x);
}

int __cdecl s_str_length(const char *s)
{
  return strlen(s);
}

int __cdecl s_str_t_length(str_t s)
{
  return strlen(s);
}

int __cdecl s_cstr_length(const char *s, int n)
{
  int len = 0;
  while (0 < n-- && *s++)
    ++len;
  return len;
}

int __cdecl s_dot_self(vector_t *v)
{
  return s_square(v->x) + s_square(v->y) + s_square(v->z);
}

double __cdecl s_square_half(double x, double *y)
{
  *y = x / 2.0;
  return x * x;
}

float __cdecl s_square_half_float(float x, float *y)
{
  *y = x / 2.0f;
  return x * x;
}

LONG __cdecl s_square_half_long(LONG x, LONG *y)
{
  *y = x / 2;
  return x * x;
}

int __cdecl s_sum_fixed_array(int a[5])
{
  return a[0] + a[1] + a[2] + a[3] + a[4];
}

int __cdecl s_pints_sum(pints_t *pints)
{
  return *pints->pi + **pints->ppi + ***pints->pppi;
}

double __cdecl s_ptypes_sum(ptypes_t *pt)
{
  return *pt->pc + *pt->ps + *pt->pl + *pt->pf + *pt->pd;
}

int __cdecl s_dot_pvectors(pvectors_t *p)
{
  return p->pu->x * (*p->pv)->x + p->pu->y * (*p->pv)->y + p->pu->z * (*p->pv)->z;
}

int __cdecl s_sum_sp(sp_t *sp)
{
  return sp->x + sp->s->x;
}

double __cdecl s_square_sun(sun_t *su)
{
  switch (su->s)
  {
  case SUN_I: return su->u.i * su->u.i;
  case SUN_F1:
  case SUN_F2: return su->u.f * su->u.f;
  case SUN_PI: return (*su->u.pi) * (*su->u.pi);
  default:
    return 0.0;
  }
}

int __cdecl s_test_list_length(test_list_t *list)
{
  return (list->t == TL_LIST
          ? 1 + s_test_list_length(list->u.tail)
          : 0);
}

int __cdecl s_sum_fixed_int_3d(int m[2][3][4])
{
  int i, j, k;
  int sum = 0;

  for (i = 0; i < 2; ++i)
    for (j = 0; j < 3; ++j)
      for (k = 0; k < 4; ++k)
        sum += m[i][j][k];

  return sum;
}

int __cdecl s_sum_conf_array(int x[], int n)
{
  int *p = x, *end = p + n;
  int sum = 0;

  while (p < end)
    sum += *p++;

  return sum;
}

int __cdecl s_sum_conf_ptr_by_conf_ptr(int n1, int *n2_then_x1, int *x2)
{
  int i;
  int sum = 0;
  if(n1 == 0)
    return 0;

  for(i = 1; i < n1; ++i)
    sum += n2_then_x1[i];

  for(i = 0; i < *n2_then_x1; ++i)
    sum += x2[i];

  return sum;
}

int __cdecl s_sum_unique_conf_array(int x[], int n)
{
  return s_sum_conf_array(x, n);
}

int __cdecl s_sum_unique_conf_ptr(int *x, int n)
{
  return x ? s_sum_conf_array(x, n) : 0;
}

int __cdecl s_sum_var_array(int x[20], int n)
{
  ok(0 <= n, "RPC sum_var_array\n");
  ok(n <= 20, "RPC sum_var_array\n");

  return s_sum_conf_array(x, n);
}

int __cdecl s_sum_complex_array(int n, refpint_t pi[])
{
  int total = 0;
  for (; n > 0; n--)
    total += *pi[n - 1];
  return total;
}

int __cdecl s_sum_blob(cs_blob_t *blob)
{
  int i, total = 0;

  for (i = 0; i < blob->n; i++)
    total += blob->ca[i];

  return total;
}

int __cdecl s_sum_data(cs_data_t *data)
{
  int i, total = 0;

  for (i = 0; i < data->blob.n; i++)
    total += data->blob.ca[i];

  return total;
}

int __cdecl s_sum_container(cs_container_t *container)
{
  int i, total = 0;

  for (i = 0; i < container->data.blob.n; i++)
    total += container->data.blob.ca[i];

  return total;
}

int __cdecl s_dot_two_vectors(vector_t vs[2])
{
  return vs[0].x * vs[1].x + vs[0].y * vs[1].y + vs[0].z * vs[1].z;
}

void __cdecl s_get_number_array(int x[20], int *n)
{
  int c[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  memcpy(x, c, sizeof(c));
  *n = ARRAY_SIZE(c);
}

int __cdecl s_sum_cs(cs_t *cs)
{
  return s_sum_conf_array(cs->ca, cs->n);
}

int __cdecl s_sum_cps(cps_t *cps)
{
  int sum = 0;
  int i;

  for (i = 0; i < *cps->pn; ++i)
    sum += cps->ca1[i];

  for (i = 0; i < 2 * cps->n; ++i)
    sum += cps->ca2[i];

  return sum;
}

int __cdecl s_sum_cpsc(cpsc_t *cpsc)
{
  int sum = 0;
  int i;
  for (i = 0; i < (cpsc->c ? cpsc->a : cpsc->b); ++i)
    sum += cpsc->ca[i];
  return sum;
}

int __cdecl s_get_cpsc(int n, cpsc_t *cpsc)
{
  int i, ret;

  cpsc->a = 2 * n;
  cpsc->b = 2;
  cpsc->c = 1;
  cpsc->ca = MIDL_user_allocate( cpsc->a * sizeof(int) );
  for (i = ret = 0; i < cpsc->a; i++) cpsc->ca[i] = i;
  for (i = ret = 0; i < cpsc->a; i++) ret += cpsc->ca[i];
  return ret;
}

int __cdecl s_square_puint(puint_t p)
{
  int n = atoi(p);
  return n * n;
}

int __cdecl s_sum_puints(puints_t *p)
{
  int sum = 0;
  int i;
  for (i = 0; i < p->n; ++i)
    sum += atoi(p->ps[i]);
  return sum;
}

int __cdecl s_sum_cpuints(cpuints_t *p)
{
  int sum = 0;
  int i;
  for (i = 0; i < p->n; ++i)
    sum += atoi(p->ps[i]);
  return sum;
}

int __cdecl s_dot_copy_vectors(vector_t u, vector_t v)
{
  return u.x * v.x + u.y * v.y + u.z * v.z;
}

int __cdecl s_square_test_us(test_us_t *tus)
{
  int n = atoi(tus->us.x);
  return n * n;
}

double __cdecl s_square_encu(encu_t *eu)
{
  switch (eu->t)
  {
  case ENCU_I: return eu->tagged_union.i * eu->tagged_union.i;
  case ENCU_F: return eu->tagged_union.f * eu->tagged_union.f;
  default:
    return 0.0;
  }
}

double __cdecl s_square_unencu(int t, unencu_t *eu)
{
  switch (t)
  {
  case ENCU_I: return eu->i * eu->i;
  case ENCU_F: return eu->f * eu->f;
  default:
    return 0.0;
  }
}

void __cdecl s_check_se2(se_t *s)
{
  ok(s->f == E2, "check_se2\n");
}

int __cdecl s_sum_parr(int *a[3])
{
  return s_sum_pcarr(a, 3);
}

int __cdecl s_sum_pcarr(int *a[], int n)
{
  int i, s = 0;
  for (i = 0; i < n; ++i)
    s += *a[i];
  return s;
}

int __cdecl s_enum_ord(e_t e)
{
  switch (e)
  {
  case E1: return 1;
  case E2: return 2;
  case E3: return 3;
  case E4: return 4;
  default:
    return 0;
  }
}

double __cdecl s_square_encue(encue_t *eue)
{
  switch (eue->t)
  {
  case E1: return eue->tagged_union.i1 * eue->tagged_union.i1;
  case E2: return eue->tagged_union.f2 * eue->tagged_union.f2;
  default:
    return 0.0;
  }
}

int __cdecl s_sum_toplev_conf_2n(int *x, int n)
{
  int sum = 0;
  int i;
  for (i = 0; i < 2 * n; ++i)
    sum += x[i];
  return sum;
}

int __cdecl s_sum_toplev_conf_cond(int *x, int a, int b, int c)
{
  int sum = 0;
  int n = c ? a : b;
  int i;
  for (i = 0; i < n; ++i)
    sum += x[i];
  return sum;
}

double __cdecl s_sum_aligns(aligns_t *a)
{
  return a->c + a->i + a->s + a->d;
}

int __cdecl s_sum_padded(padded_t *p)
{
  return p->i + p->c;
}

int __cdecl s_sum_padded2(padded_t ps[2])
{
  return s_sum_padded(&ps[0]) + s_sum_padded(&ps[1]);
}

int __cdecl s_sum_padded_conf(padded_t *ps, int n)
{
  int sum = 0;
  int i;
  for (i = 0; i < n; ++i)
    sum += s_sum_padded(&ps[i]);
  return sum;
}

int __cdecl s_sum_bogus(bogus_t *b)
{
  return *b->h.p1 + *b->p2 + *b->p3 + b->c;
}

void __cdecl s_check_null(int *null)
{
  ok(!null, "RPC check_null\n");
}

int __cdecl s_str_struct_len(str_struct_t *s)
{
  return lstrlenA(s->s);
}

int __cdecl s_wstr_struct_len(wstr_struct_t *s)
{
  return lstrlenW(s->s);
}

int __cdecl s_sum_doub_carr(doub_carr_t *dc)
{
  int i, j;
  int sum = 0;
  for (i = 0; i < dc->n; ++i)
    for (j = 0; j < dc->a[i]->n; ++j)
      sum += dc->a[i]->a[j];
  return sum;
}

void __cdecl s_make_pyramid_doub_carr(unsigned char n, doub_carr_t **dc)
{
  doub_carr_t *t;
  int i, j;
  t = MIDL_user_allocate(FIELD_OFFSET(doub_carr_t, a[n]));
  t->n = n;
  for (i = 0; i < n; ++i)
  {
    int v = i + 1;
    t->a[i] = MIDL_user_allocate(FIELD_OFFSET(doub_carr_1_t, a[v]));
    t->a[i]->n = v;
    for (j = 0; j < v; ++j)
      t->a[i]->a[j] = j + 1;
  }
  *dc = t;
}

unsigned __cdecl s_hash_bstr(bstr_t b)
{
  short n = b[-1];
  short *s = b;
  unsigned hash = 0;
  short i;
  for (i = 0; i < n; ++i)
    hash = 5 * hash + (unsigned) s[i];
  return hash;
}

void __cdecl s_get_a_bstr(bstr_t *b)
{
  bstr_t bstr;
  short str[] = {5, 'W', 'i', 'n', 'e', 0};
  bstr = malloc(sizeof(str));
  memcpy(bstr, str, sizeof(str));
  *b = bstr + 1;
}

void __cdecl s_get_name(name_t *name)
{
  const char bossman[] = "Jeremy White";
  memcpy(name->name, bossman, min(name->size, sizeof(bossman)));
  /* ensure nul-termination */
  if (name->size < sizeof(bossman))
    name->name[name->size - 1] = 0;
}

void __cdecl s_get_names(int *n, str_array_t *names)
{
  str_array_t list;

  list = MIDL_user_allocate(2 * sizeof(list[0]));
  list[0] = MIDL_user_allocate(6);
  strcpy(list[0], "Hello");
  list[1] = MIDL_user_allocate(7);
  strcpy(list[1], "World!");

  *names = list;
  *n = 2;
}

void __cdecl s_get_namesw(int *n, wstr_array_t *names)
{
  wstr_array_t list;

  list = MIDL_user_allocate(2 * sizeof(list[0]));
  list[0] = MIDL_user_allocate(sizeof(L"Hello"));
  lstrcpyW(list[0], L"Hello");
  list[1] = MIDL_user_allocate(sizeof(L"World!"));
  lstrcpyW(list[1], L"World!");

  *names = list;
  *n = 2;
}

int __cdecl s_sum_pcarr2(int n, int **pa)
{
  return s_sum_conf_array(*pa, n);
}

int __cdecl s_sum_L1_norms(int n, vector_t *vs)
{
  int i;
  int sum = 0;
  for (i = 0; i < n; ++i)
    sum += abs(vs[i].x) + abs(vs[i].y) + abs(vs[i].z);
  return sum;
}

s123_t * __cdecl s_get_s123(void)
{
  s123_t *s = MIDL_user_allocate(sizeof *s);
  s->f1 = 1;
  s->f2 = 2;
  s->f3 = 3;
  return s;
}

str_t __cdecl s_get_filename(void)
{
    void *ptr = MIDL_user_allocate(strlen(__FILE__) + 1);
    strcpy(ptr, __FILE__);
    return (char *)ptr;
}

int __cdecl s_echo_ranged_int(int i, int j, int k)
{
    return min( 100, i + j + k );
}

int __cdecl s_echo_ranged_int2(int i)
{
    return i;
}

void __cdecl s_get_ranged_enum(renum_t *re)
{
    *re = RE3;
}

void __cdecl s_context_handle_test(void)
{
    NDR_SCONTEXT h;
    RPC_BINDING_HANDLE binding;
    RPC_STATUS status;
    unsigned char buf[20];
    static RPC_SERVER_INTERFACE server_if =
    {
        sizeof(RPC_SERVER_INTERFACE),
        {{0x00000000,0x4114,0x0704,{0x23,0x01,0x00,0x00,0x00,0x00,0x00,0x00}},{1,0}},
        {{0x8a885d04,0x1ceb,0x11c9,{0x9f,0xe8,0x08,0x00,0x2b,0x10,0x48,0x60}},{2,0}},
        NULL,
        0,
        0,
        0,
        0,
        0,
    };

    binding = I_RpcGetCurrentCallHandle();
    ok(binding != NULL, "I_RpcGetCurrentCallHandle returned NULL\n");

    if (!pNDRSContextMarshall2 || !pNDRSContextUnmarshall2)
    {
        win_skip("NDRSContextMarshall2 or NDRSContextUnmarshall2 not exported from rpcrt4.dll\n");
        return;
    }

    h = pNDRSContextUnmarshall2(binding, NULL, NDR_LOCAL_DATA_REPRESENTATION, NULL, 0);
    ok(h != NULL, "NDRSContextUnmarshall2 returned NULL\n");

    /* marshal a context handle with NULL userContext */
    memset(buf, 0xcc, sizeof(buf));
    pNDRSContextMarshall2(binding, h, buf, NULL, NULL, 0);
    ok(*(ULONG *)buf == 0, "attributes should have been set to 0 instead of 0x%lx\n", *(ULONG *)buf);
    ok(UuidIsNil((UUID *)&buf[4], &status), "uuid should have been nil\n");

    h = pNDRSContextUnmarshall2(binding, NULL, NDR_LOCAL_DATA_REPRESENTATION, NULL, 0);
    ok(h != NULL, "NDRSContextUnmarshall2 returned NULL\n");

    /* marshal a context handle with non-NULL userContext */
    memset(buf, 0xcc, sizeof(buf));
    h->userContext = (void *)0xdeadbeef;
    pNDRSContextMarshall2(binding, h, buf, NULL, NULL, 0);
    ok(*(ULONG *)buf == 0, "attributes should have been set to 0 instead of 0x%lx\n", *(ULONG *)buf);
    ok(!UuidIsNil((UUID *)&buf[4], &status), "uuid should not have been nil\n");

    /* raises ERROR_INVALID_HANDLE exception on Vista upwards */
    if (0)
    {
    h = pNDRSContextUnmarshall2(binding, buf, NDR_LOCAL_DATA_REPRESENTATION, NULL, 0);
    ok(h != NULL, "NDRSContextUnmarshall2 returned NULL\n");
    ok(h->userContext == (void *)0xdeadbeef, "userContext of interface didn't unmarshal properly: %p\n", h->userContext);

    /* marshal a context handle with an interface specified */
    h = pNDRSContextUnmarshall2(binding, NULL, NDR_LOCAL_DATA_REPRESENTATION, &server_if.InterfaceId, 0);
    ok(h != NULL, "NDRSContextUnmarshall2 returned NULL\n");

    memset(buf, 0xcc, sizeof(buf));
    h->userContext = (void *)0xcafebabe;
    pNDRSContextMarshall2(binding, h, buf, NULL, &server_if.InterfaceId, 0);
    ok(*(ULONG *)buf == 0, "attributes should have been set to 0 instead of 0x%lx\n", *(ULONG *)buf);
    ok(!UuidIsNil((UUID *)&buf[4], &status), "uuid should not have been nil\n");

    h = pNDRSContextUnmarshall2(binding, buf, NDR_LOCAL_DATA_REPRESENTATION, &server_if.InterfaceId, 0);
    ok(h != NULL, "NDRSContextUnmarshall2 returned NULL\n");
    ok(h->userContext == (void *)0xcafebabe, "userContext of interface didn't unmarshal properly: %p\n", h->userContext);
    }

    /* test same interface data, but different pointer */
    /* raises ERROR_INVALID_HANDLE exception */
    if (0)
    {
        RPC_SERVER_INTERFACE server_if_clone = server_if;

        pNDRSContextUnmarshall2(binding, buf, NDR_LOCAL_DATA_REPRESENTATION, &server_if_clone.InterfaceId, 0);
    }

    /* test different interface data, but different pointer */
    /* raises ERROR_INVALID_HANDLE exception */
    if (0)
    {
        static RPC_SERVER_INTERFACE server_if2 =
        {
            sizeof(RPC_SERVER_INTERFACE),
            {{0x00000000,0x4114,0x0704,{0x23,0x01,0x00,0x00,0x00,0x00,0x00,0x00}},{1,0}},
            {{0x8a885d04,0x1ceb,0x11c9,{0x9f,0xe8,0x08,0x00,0x2b,0x10,0x48,0x60}},{2,0}},
            NULL,
            0,
            0,
            0,
            0,
            0,
        };
        pNDRSContextMarshall2(binding, h, buf, NULL, &server_if.InterfaceId, 0);

        pNDRSContextUnmarshall2(binding, buf, NDR_LOCAL_DATA_REPRESENTATION, &server_if2.InterfaceId, 0);
    }

    binding = NULL;
    status = RpcBindingServerFromClient(NULL, &binding);

    ok(status == RPC_S_OK, "expected RPC_S_OK got %lu\n", status);
    ok(binding != NULL, "binding is NULL\n");

    if (status == RPC_S_OK && binding != NULL)
    {
        unsigned char* string_binding = NULL;
        unsigned char* object_uuid = NULL;
        unsigned char* protseq = NULL;
        unsigned char* network_address = NULL;
        unsigned char* endpoint = NULL;
        unsigned char* network_options = NULL;

        status = RpcBindingToStringBindingA(binding, &string_binding);
        ok(status == RPC_S_OK, "expected RPC_S_OK got %lu\n", status);
        ok(string_binding != NULL, "string_binding is NULL\n");

        status = RpcStringBindingParseA(string_binding, &object_uuid, &protseq, &network_address, &endpoint, &network_options);
        ok(status == RPC_S_OK, "expected RPC_S_OK got %lu\n", status);
        ok(protseq != NULL && *protseq != '\0', "protseq is %s\n", protseq);
        ok(network_address != NULL && *network_address != '\0', "network_address is %s\n", network_address);

        todo_wine
        {
            ok(object_uuid != NULL && *object_uuid == '\0', "object_uuid is %s\n", object_uuid);
            ok(endpoint != NULL && *endpoint == '\0', "endpoint is %s\n", endpoint);
            ok(network_options != NULL && *network_options == '\0', "network_options is %s\n", network_options);
        }

        RpcStringFreeA(&string_binding);
        RpcStringFreeA(&object_uuid);
        RpcStringFreeA(&protseq);
        RpcStringFreeA(&network_address);
        RpcStringFreeA(&endpoint);
        RpcStringFreeA(&network_options);
        RpcBindingFree(&binding);
    }
}

void __cdecl s_get_numbers(int length, int size, pints_t n[])
{
    int i;
    for (i = 0; i < length; i++)
    {
        n[i].pi = midl_user_allocate(sizeof(*n[i].pi));
        *n[i].pi = i;
        n[i].ppi = NULL;
        n[i].pppi = NULL;
    }
}

void __cdecl s_get_numbers_struct(numbers_struct_t **ns)
{
    int i;
    *ns = midl_user_allocate(FIELD_OFFSET(numbers_struct_t, numbers[5]));
    if (!*ns) return;
    (*ns)->length = 5;
    (*ns)->size = 5;
    for (i = 0; i < (*ns)->length; i++)
    {
        (*ns)->numbers[i].pi = NULL;
        (*ns)->numbers[i].ppi = NULL;
        (*ns)->numbers[i].pppi = NULL;
    }
    (*ns)->numbers[0].pi = midl_user_allocate(sizeof(*(*ns)->numbers[i].pi));
    *(*ns)->numbers[0].pi = 5;
}

void __cdecl s_full_pointer_test(int *a, int *b)
{
    ok(*a == 42, "Expected *a to be 42 instead of %d\n", *a);
    ok(*b == 42, "Expected *b to be 42 instead of %d\n", *a);
    ok(a == b, "Expected a (%p) to point to the same memory as b (%p)\n", a, b);
}

void __cdecl s_full_pointer_null_test(int *a, int *b)
{
    ok(*a == 42, "Expected *a to be 42 instead of %d\n", *a);
    ok(b == NULL, "Expected b to be NULL instead of %p\n", b);
}

void __cdecl s_stop(void)
{
  if (!stop_wait_event)
  {
    ok(RPC_S_OK == RpcMgmtStopServerListening(NULL), "RpcMgmtStopServerListening\n");
    ok(RPC_S_OK == RpcServerUnregisterIf(NULL, NULL, FALSE), "RpcServerUnregisterIf\n");
  }
  ok(SetEvent(stop_event), "SetEvent\n");
  if (stop_wait_event)
  {
    DWORD ret;
    ret = WaitForSingleObject(stop_wait_event, 10000);
    ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject\n");
  }
}

void __cdecl s_stop_autolisten(void)
{
    RPC_STATUS status;
    status = RpcServerUnregisterIf(NULL, NULL, FALSE);
    todo_wine
    ok(status == RPC_S_UNKNOWN_MGR_TYPE, "got %lu\n", status);
}

void __cdecl s_ip_test(ipu_t *a)
{
    STATSTG st;
    HRESULT hr;

    hr = IStream_Stat(a->tagged_union.stream, &st, STATFLAG_NONAME);
    ok(hr == S_OK, "got %#lx\n", hr);
}

int __cdecl s_sum_ptr_array(int *a[2])
{
    return *a[0] + *a[1];
}

int __cdecl s_sum_array_ptr(int (*a)[2])
{
    return (*a)[0] + (*a)[1];
}

ctx_handle_t __cdecl s_get_handle(void)
{
    return (ctx_handle_t)0xdeadbeef;
}

void __cdecl s_get_handle_by_ptr(ctx_handle_t *r)
{
    *r = (ctx_handle_t)0xdeadbeef;
}

void __cdecl s_test_handle(ctx_handle_t ctx_handle)
{
    ok(ctx_handle == (ctx_handle_t)0xdeadbeef, "Unexpected ctx_handle %p\n", ctx_handle);
}

struct test_thread_params
{
    unsigned int protseq;
    RPC_BINDING_HANDLE binding;
};

static DWORD CALLBACK test_I_RpcBindingInqLocalClientPID_thread_func(void *args)
{
    struct test_thread_params *params = (struct test_thread_params *)args;
    RPC_STATUS status;
    ULONG pid;

    winetest_push_context("%s", client_test_name);

    status = I_RpcBindingInqLocalClientPID(NULL, &pid);
    ok(status == RPC_S_NO_CALL_ACTIVE, "Got unexpected %ld.\n", status);

    /* Other protocol sequences throw exceptions */
    if (params->protseq == RPC_PROTSEQ_LRPC)
    {
        status = I_RpcBindingInqLocalClientPID(params->binding, &pid);
        ok(status == RPC_S_OK, "Got unexpected %ld.\n", status);
        ok(pid == client_info.dwProcessId, "Got unexpected pid.\n");
    }

    winetest_pop_context();
    return 0;
}

void __cdecl s_test_I_RpcBindingInqLocalClientPID(unsigned int protseq, RPC_BINDING_HANDLE binding)
{
    struct test_thread_params params;
    RPC_STATUS status;
    HANDLE thread;
    ULONG pid;

    winetest_push_context("%s", client_test_name);

    /* Crash on Windows */
    if (0)
    {
    status = I_RpcBindingInqLocalClientPID(NULL, NULL);
    ok(status == RPC_S_INVALID_ARG, "Got unexpected %ld.\n", status);

    status = I_RpcBindingInqLocalClientPID(binding, NULL);
    ok(status == RPC_S_INVALID_ARG, "Got unexpected %ld.\n", status);
    }

    status = I_RpcBindingInqLocalClientPID(NULL, &pid);
    if (protseq == RPC_PROTSEQ_LRPC)
    {
        ok(status == RPC_S_OK, "Got unexpected %ld.\n", status);
        ok(pid == client_info.dwProcessId, "Got unexpected pid.\n");
    }
    else
    {
        ok(status == RPC_S_INVALID_BINDING, "Got unexpected %ld.\n", status);
    }

    if (protseq == RPC_PROTSEQ_LRPC) /* Other protocol sequences throw exceptions */
    {
        status = I_RpcBindingInqLocalClientPID(binding, &pid);
        ok(status == RPC_S_OK, "Got unexpected %ld.\n", status);
        ok(pid == client_info.dwProcessId, "Got unexpected pid.\n");
    }

    params.protseq = protseq;
    params.binding = binding;
    thread = CreateThread(NULL, 0, test_I_RpcBindingInqLocalClientPID_thread_func, &params, 0, NULL);
    WaitForSingleObject(thread, INFINITE);
    CloseHandle(thread);

    winetest_pop_context();
}

int __cdecl s_add(handle_t binding, int a, int b)
{
  ok(binding != NULL, "explicit handle is NULL\n");
  return a + b;
}

int __cdecl s_getNum(int a, handle_t binding)
{
  ok(binding != NULL, "explicit handle is NULL\n");
  return a + 2;
}

void __cdecl s_Shutdown(handle_t binding)
{
    RPC_STATUS status;
    ULONG pid = 0;
    ok(binding != NULL, "explicit handle is NULL\n");

    status = I_RpcBindingInqLocalClientPID(binding, &pid);
    ok(status == RPC_S_OK, "Got unexpected %ld.\n", status);
    ok(pid == client_info.dwProcessId, "Got unexpected pid: %ld client pid: %ld.\n", pid, client_info.dwProcessId);
    ok(SetEvent(stop_event), "SetEvent\n");
}

void __RPC_USER ctx_handle_t_rundown(ctx_handle_t ctx_handle)
{
    ok(ctx_handle == (ctx_handle_t)0xdeadbeef, "Unexpected ctx_handle %p\n", ctx_handle);
}

static void
make_cmdline(char buffer[MAX_PATH], const char *test)
{
  sprintf(buffer, "%s server %s", progname, test);
}

static void
run_client(const char *test)
{
  char cmdline[MAX_PATH];
  STARTUPINFOA startup;

  memset(&startup, 0, sizeof startup);
  startup.cb = sizeof startup;

  client_test_name = test;
  make_cmdline(cmdline, test);
  ok(CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &client_info), "CreateProcess\n");
  wait_child_process(&client_info);
}

static void
basic_tests(void)
{
  char string[] = "I am a string";
  WCHAR wstring[] = L"I am a wstring";
  int f[5] = {1, 3, 0, -2, -4};
  vector_t a = {1, 3, 7};
  vector_t vec1 = {4, -2, 1}, vec2 = {-5, 2, 3}, *pvec2 = &vec2;
  pvectors_t pvecs = {&vec1, &pvec2};
  sp_inner_t spi = {42};
  sp_t sp = {-13, &spi};
  aligns_t aligns;
  pints_t pints;
  ptypes_t ptypes;
  padded_t padded;
  padded_t padded2[2];
  bogus_t bogus;
  int i1, i2, i3, *pi2, *pi3, **ppi3;
  double u, v;
  float s, t;
  LONG q, r;
  short h;
  char c;
  int x;
  hyper y;
  str_struct_t ss = {string};
  wstr_struct_t ws = {wstring};
  str_t str;
  se_t se;
  renum_t re;
  chars_t cs1 = { 2, 4, 6, 8, 10 }, cs2 = { -1, -2, -3, -4, -5 };
  ints_t is1 = { 2, 4 }, is2 = { 5, 8 };
  flts_t fs1 = { 3, 5.5 }, fs2 = { 8, 2.3 };
  dbls_t ds1 = { 9, -2.5 }, ds2 = { 2, -4.2 };

  ok(int_return() == INT_CODE, "RPC int_return\n");

  ok(square(7) == 49, "RPC square\n");
  x = sum(23, -4);
  ok(x == 19, "RPC sum got %d\n", x);
  c = sum_char(-23, 50);
  ok(c == 27, "RPC sum_char got %d\n", (int)c);
  h = sum_short(1122, -344);
  ok(h == 778, "RPC sum_short got %d\n", (int)h);
  x = sum_float(123.45, -32.2);
  ok(x == 91, "RPC sum_float got %d\n", x);
  x = sum_double_int(-78, 148.46);
  ok(x == 70, "RPC sum_double_int got %d\n", x);
  y = sum_hyper((hyper)0x12345678 << 16, (hyper)0x33557799 << 16);
  ok(y == (hyper)0x4589ce11 << 16, "RPC hyper got %s\n", wine_dbgstr_longlong(y));
  x = sum_hyper_int((hyper)0x24242424 << 16, -((hyper)0x24241212 << 16));
  ok(x == 0x12120000, "RPC hyper_int got 0x%x\n", x);
  x = sum_char_hyper( 12, ((hyper)0x42424242 << 32) | 0x33334444 );
  ok(x == 0x33334450, "RPC char_hyper got 0x%x\n", x);
  x = sum_chars( 4, cs1, cs2 );
  ok(x == 60, "got %d\n", x);
  x = sum_ints( 2, is1, is2 );
  ok(x == 38, "got %d\n", x);
  x = sum_flts( 3, fs1, fs2 );
  ok(x == 56, "got %d\n", x);
  x = sum_dbls( 7, ds1, ds2 );
  ok(x == 30, "got %d\n", x);
  x = sum_iiff( 12, 23, 3.4, 4.7 );
  ok(x == 43, "got %d\n", x);
  x = sum_ifif( 12, 11.2, 23, 34.5 );
  ok(x == 80, "got %d\n", x);
  x = sum_iidd( 6, 5, 4.3, 2.1 );
  ok(x == 17, "got %d\n", x);
  x = sum_idid( 55, 44.55, 33, 22.44 );
  ok(x == 154, "got %d\n", x);
  x = sum_ififififififif( 1, 2.1, 3, 4.2, 5, 6.3, 7, 8.4, 9, 10.5, 11, 12.2, 13, 14.9 );
  ok(x == 107, "got %d\n", x);
  x = sum_ididididididid( -1, -2.1, -3, -4, -5, -6.7, -8, -9.1, -11, -12.3, -13, -14.2, -15, -16.3 );
  ok(x == -120, "got %d\n", x);
  x = sum_idfdifdfifdfidifdidf( 2, 1.2, 2.1, 2.3, 4, 3.2, 4.5, 5.5, -3, -2.2, -4.4, -5.5, 6, 6.4, -3, 8.1, 9.2, 7, -10.3, 12.4 );
  ok(x == 45, "got %d\n", x);
  x = sum_ididididididididididid( 2, 3, 4, 5, 6, 7, 8, 9, -1, -2, -3, -4, -5, -6, -7, -8, -9, 5 );
  ok(x == 4, "got %d\n", x);
  x = sum_iidiidiidiidiidiidiidiidiidiidiid( 11, 22, 33.1, 44, 55, 66.1, 77, 88, 99.1, 111, 222, 333.1, 444,
                                             555, 666.1, 777, 888, 999.1, 1, 2, 3.1, 4, 5, 6.1, 7, 8, 9.1 );
  ok(x == 5535, "got %d\n", x);
  x = sum_iyiy( 1, 1234567890, 2, -1234567800 );
  ok(x == 93, "got %d\n", x);

  x = 0;
  square_out(11, &x);
  ok(x == 121, "RPC square_out\n");

  x = 5;
  square_ref(&x);
  ok(x == 25, "RPC square_ref\n");

  ok(str_length(string) == strlen(string), "RPC str_length\n");
  ok(str_t_length(string) == strlen(string), "RPC str_length\n");
  ok(dot_self(&a) == 59, "RPC dot_self\n");

  ok(str_struct_len(&ss) == lstrlenA(string), "RPC str_struct_len\n");
  ok(wstr_struct_len(&ws) == lstrlenW(wstring), "RPC str_struct_len\n");

  v = 0.0;
  u = square_half(3.0, &v);
  ok(u == 9.0, "RPC square_half\n");
  ok(v == 1.5, "RPC square_half\n");

  t = 0.0f;
  s = square_half_float(3.0f, &t);
  ok(s == 9.0f, "RPC square_half_float\n");
  ok(t == 1.5f, "RPC square_half_float\n");

  r = 0;
  q = square_half_long(3, &r);
  ok(q == 9, "RPC square_half_long\n");
  ok(r == 1, "RPC square_half_long\n");

  i1 = 19;
  i2 = -3;
  i3 = -29;
  pi2 = &i2;
  pi3 = &i3;
  ppi3 = &pi3;
  pints.pi = &i1;
  pints.ppi = &pi2;
  pints.pppi = &ppi3;
  ok(pints_sum(&pints) == -13, "RPC pints_sum\n");

  c = 10;
  h = 3;
  q = 14;
  s = -5.0f;
  u = 11.0;
  ptypes.pc = &c;
  ptypes.ps = &h;
  ptypes.pl = &q;
  ptypes.pf = &s;
  ptypes.pd = &u;
  ok(ptypes_sum(&ptypes) == 33.0, "RPC ptypes_sum\n");

  ok(dot_pvectors(&pvecs) == -21, "RPC dot_pvectors\n");
  ok(dot_copy_vectors(vec1, vec2) == -21, "RPC dot_copy_vectors\n");
  ok(sum_fixed_array(f) == -2, "RPC sum_fixed_array\n");
  ok(sum_sp(&sp) == 29, "RPC sum_sp\n");

  ok(enum_ord(E1) == 1, "RPC enum_ord\n");
  ok(enum_ord(E2) == 2, "RPC enum_ord\n");
  ok(enum_ord(E3) == 3, "RPC enum_ord\n");
  ok(enum_ord(E4) == 4, "RPC enum_ord\n");

  se.f = E2;
  check_se2(&se);

  memset(&aligns, 0, sizeof(aligns));
  aligns.c = 3;
  aligns.i = 4;
  aligns.s = 5;
  aligns.d = 6.0;
  ok(sum_aligns(&aligns) == 18.0, "RPC sum_aligns\n");

  padded.i = -3;
  padded.c = 8;
  ok(sum_padded(&padded) == 5, "RPC sum_padded\n");
  padded2[0].i = -5;
  padded2[0].c = 1;
  padded2[1].i = 3;
  padded2[1].c = 7;
  ok(sum_padded2(padded2) == 6, "RPC sum_padded2\n");
  padded2[0].i = -5;
  padded2[0].c = 1;
  padded2[1].i = 3;
  padded2[1].c = 7;
  ok(sum_padded_conf(padded2, 2) == 6, "RPC sum_padded_conf\n");

  i1 = 14;
  i2 = -7;
  i3 = -4;
  bogus.h.p1 = &i1;
  bogus.p2 = &i2;
  bogus.p3 = &i3;
  bogus.c = 9;
  ok(sum_bogus(&bogus) == 12, "RPC sum_bogus\n");

  check_null(NULL);

  str = get_filename();
  ok(!strcmp(str, __FILE__), "get_filename() returned %s instead of %s\n", str, __FILE__);
  midl_user_free(str);

  x = echo_ranged_int(0,0,0);
  ok(x == 0, "echo_ranged_int() returned %d instead of 0\n", x);
  x = echo_ranged_int(10,20,100);
  ok(x == 100, "echo_ranged_int() returned %d instead of 100\n", x);
  x = echo_ranged_int2(40);
  ok(x == 40, "echo_ranged_int() returned %d instead of 40\n", x);

  if (!old_windows_version)
  {
      re = 0xdeadbeef;
      get_ranged_enum(&re);
      ok(re == RE3 ||
         broken(re == MAKELONG(re, 0xdead)), /* Win 8, Win 10 */
         "get_ranged_enum() returned %x instead of RE3\n", re);
  }
}

static void
union_tests(void)
{
  encue_t eue;
  encu_t eu;
  unencu_t uneu;
  sun_t su;
  ipu_t ipu;
  LONG ref;
  int i;

  su.s = SUN_I;
  su.u.i = 9;
  ok(square_sun(&su) == 81.0, "RPC square_sun\n");

  su.s = SUN_F1;
  su.u.f = 5.0;
  ok(square_sun(&su) == 25.0, "RPC square_sun\n");

  su.s = SUN_F2;
  su.u.f = -2.0;
  ok(square_sun(&su) == 4.0, "RPC square_sun\n");

  su.s = SUN_PI;
  su.u.pi = &i;
  i = 11;
  ok(square_sun(&su) == 121.0, "RPC square_sun\n");

  eu.t = ENCU_I;
  eu.tagged_union.i = 7;
  ok(square_encu(&eu) == 49.0, "RPC square_encu\n");

  eu.t = ENCU_F;
  eu.tagged_union.f = 3.0;
  ok(square_encu(&eu) == 9.0, "RPC square_encu\n");

  uneu.i = 4;
  ok(square_unencu(ENCU_I, &uneu) == 16.0, "RPC square_unencu\n");

  uneu.f = 5.0;
  ok(square_unencu(ENCU_F, &uneu) == 25.0, "RPC square_unencu\n");

  eue.t = E1;
  eue.tagged_union.i1 = 8;
  ok(square_encue(&eue) == 64.0, "RPC square_encue\n");

  eue.t = E2;
  eue.tagged_union.f2 = 10.0;
  ok(square_encue(&eue) == 100.0, "RPC square_encue\n");

  CoInitializeEx(NULL, COINIT_MULTITHREADED);

  CreateStreamOnHGlobal(NULL, TRUE, &ipu.tagged_union.stream);
  ip_test(&ipu);
  ref = IStream_Release(ipu.tagged_union.stream);
  ok(!ref, "got %lu refs\n", ref);

  CoUninitialize();
}

static test_list_t *
null_list(void)
{
  test_list_t *n = malloc(sizeof *n);
  n->t = TL_NULL;
  n->u.x = 0;
  return n;
}

static test_list_t *
make_list(test_list_t *tail)
{
  test_list_t *n = malloc(sizeof *n);
  n->t = TL_LIST;
  n->u.tail = tail;
  return n;
}

static void
free_list(test_list_t *list)
{
  if (list->t == TL_LIST)
    free_list(list->u.tail);
  free(list);
}

ULONG __RPC_USER
puint_t_UserSize(ULONG *flags, ULONG start, puint_t *p)
{
  return start + sizeof(int);
}

unsigned char * __RPC_USER
puint_t_UserMarshal(ULONG *flags, unsigned char *buffer, puint_t *p)
{
  int n = atoi(*p);
  memcpy(buffer, &n, sizeof n);
  return buffer + sizeof n;
}

unsigned char * __RPC_USER
puint_t_UserUnmarshal(ULONG *flags, unsigned char *buffer, puint_t *p)
{
  int n;
  memcpy(&n, buffer, sizeof n);
  *p = malloc(10);
  sprintf(*p, "%d", n);
  return buffer + sizeof n;
}

void __RPC_USER
puint_t_UserFree(ULONG *flags, puint_t *p)
{
  free(*p);
}

ULONG __RPC_USER
us_t_UserSize(ULONG *flags, ULONG start, us_t *pus)
{
  return start + sizeof(struct wire_us);
}

unsigned char * __RPC_USER
us_t_UserMarshal(ULONG *flags, unsigned char *buffer, us_t *pus)
{
  struct wire_us wus;
  wus.x = atoi(pus->x);
  memcpy(buffer, &wus, sizeof wus);
  return buffer + sizeof wus;
}

unsigned char * __RPC_USER
us_t_UserUnmarshal(ULONG *flags, unsigned char *buffer, us_t *pus)
{
  struct wire_us wus;
  memcpy(&wus, buffer, sizeof wus);
  pus->x = malloc(10);
  sprintf(pus->x, "%d", wus.x);
  return buffer + sizeof wus;
}

void __RPC_USER
us_t_UserFree(ULONG *flags, us_t *pus)
{
  free(pus->x);
}

ULONG __RPC_USER
bstr_t_UserSize(ULONG *flags, ULONG start, bstr_t *b)
{
  return start + FIELD_OFFSET(user_bstr_t, data[(*b)[-1]]);
}

unsigned char * __RPC_USER
bstr_t_UserMarshal(ULONG *flags, unsigned char *buffer, bstr_t *b)
{
  wire_bstr_t wb = (wire_bstr_t) buffer;
  wb->n = (*b)[-1];
  memcpy(&wb->data, *b, wb->n * sizeof wb->data[0]);
  return buffer + FIELD_OFFSET(user_bstr_t, data[wb->n]);
}

unsigned char * __RPC_USER
bstr_t_UserUnmarshal(ULONG *flags, unsigned char *buffer, bstr_t *b)
{
  wire_bstr_t wb = (wire_bstr_t) buffer;
  short *data = malloc((wb->n + 1) * sizeof *data);
  data[0] = wb->n;
  memcpy(&data[1], wb->data, wb->n * sizeof data[1]);
  *b = &data[1];
  return buffer + FIELD_OFFSET(user_bstr_t, data[wb->n]);
}

void __RPC_USER
bstr_t_UserFree(ULONG *flags, bstr_t *b)
{
  free(&((*b)[-1]));
}

static void
pointer_tests(void)
{
  int a[] = {1, 2, 3, 4};
  char p1[] = "11";
  test_list_t *list = make_list(make_list(make_list(null_list())));
  test_us_t tus = {{p1}};
  int *pa[4];
  puints_t pus;
  cpuints_t cpus;
  short bstr_data[] = { 5, 'H', 'e', 'l', 'l', 'o' };
  bstr_t bstr = &bstr_data[1], bstr2;
  name_t name;
  void *buffer;
  int *pa2;
  s123_t *s123;
  int val = 42;

  ok(test_list_length(list) == 3, "RPC test_list_length\n");
  ok(square_puint(p1) == 121, "RPC square_puint\n");
  pus.n = 4;
  pus.ps = malloc(pus.n * sizeof pus.ps[0]);
  pus.ps[0] = strdup("5");
  pus.ps[1] = strdup("6");
  pus.ps[2] = strdup("7");
  pus.ps[3] = strdup("8");
  ok(sum_puints(&pus) == 26, "RPC sum_puints\n");
  free(pus.ps[0]);
  free(pus.ps[1]);
  free(pus.ps[2]);
  free(pus.ps[3]);
  free(pus.ps);
  cpus.n = 4;
  cpus.ps = malloc(cpus.n * sizeof cpus.ps[0]);
  cpus.ps[0] = strdup("5");
  cpus.ps[1] = strdup("6");
  cpus.ps[2] = strdup("7");
  cpus.ps[3] = strdup("8");
  ok(sum_cpuints(&cpus) == 26, "RPC sum_puints\n");
  free(cpus.ps[0]);
  free(cpus.ps[1]);
  free(cpus.ps[2]);
  free(cpus.ps[3]);
  free(cpus.ps);
  ok(square_test_us(&tus) == 121, "RPC square_test_us\n");

  pa[0] = &a[0];
  pa[1] = &a[1];
  pa[2] = &a[2];
  ok(sum_parr(pa) == 6, "RPC sum_parr\n");

  pa[0] = &a[0];
  pa[1] = &a[1];
  pa[2] = &a[2];
  pa[3] = &a[3];
  ok(sum_pcarr(pa, 4) == 10, "RPC sum_pcarr\n");

  ok(hash_bstr(bstr) == s_hash_bstr(bstr), "RPC hash_bstr_data\n");

  get_a_bstr(&bstr);
  s_get_a_bstr(&bstr2);
  ok(!lstrcmpW((LPCWSTR)bstr, (LPCWSTR)bstr2), "bstr mismatch\n");
  free(bstr - 1);
  free(bstr2 - 1);

  free_list(list);

  if (!old_windows_version)
  {
      int n;
      str_array_t names;
      wstr_array_t namesw;

      name.size = 10;
      name.name = buffer = calloc(1, name.size);
      get_name(&name);
      ok(name.name == buffer, "[in,out] pointer should have stayed as %p but instead changed to %p\n", name.name, buffer);
      ok(!strcmp(name.name, "Jeremy Wh"), "name didn't unmarshall properly, expected \"Jeremy Wh\", but got \"%s\"\n", name.name);
      free(name.name);

      n = -1;
      names = NULL;
      get_names(&n, &names);
      ok(n == 2, "expected 2, got %d\n", n);
      ok(!strcmp(names[0], "Hello"), "expected Hello, got %s\n", names[0]);
      ok(!strcmp(names[1], "World!"), "expected World!, got %s\n", names[1]);
      MIDL_user_free(names[0]);
      MIDL_user_free(names[1]);
      MIDL_user_free(names);

      n = -1;
      namesw = NULL;
      get_namesw(&n, &namesw);
      ok(n == 2, "expected 2, got %d\n", n);
      ok(!lstrcmpW(namesw[0], L"Hello"), "expected Hello, got %s\n", wine_dbgstr_w(namesw[0]));
      ok(!lstrcmpW(namesw[1], L"World!"), "expected World!, got %s\n", wine_dbgstr_w(namesw[1]));
      MIDL_user_free(namesw[0]);
      MIDL_user_free(namesw[1]);
      MIDL_user_free(namesw);
  }

  pa2 = a;
  ok(sum_pcarr2(4, &pa2) == 10, "RPC sum_pcarr2\n");

  s123 = get_s123();
  ok(s123->f1 == 1 && s123->f2 == 2 && s123->f3 == 3, "RPC get_s123\n");
  MIDL_user_free(s123);

  full_pointer_test(&val, &val);
  full_pointer_null_test(&val, NULL);
}

static int
check_pyramid_doub_carr(doub_carr_t *dc)
{
  int i, j;
  for (i = 0; i < dc->n; ++i)
    for (j = 0; j < dc->a[i]->n; ++j)
      if (dc->a[i]->a[j] != j + 1)
        return FALSE;
  return TRUE;
}

static void
free_pyramid_doub_carr(doub_carr_t *dc)
{
  int i;
  for (i = 0; i < dc->n; ++i)
    MIDL_user_free(dc->a[i]);
  MIDL_user_free(dc);
}

static void
array_tests(void)
{
  int m[2][3][4] =
  {
    {{1, 2, 3, 4}, {-1, -3, -5, -7}, {0, 2, 4, 6}},
    {{1, -2, 3, -4}, {2, 3, 5, 7}, {-4, -1, -14, 4114}}
  };
  int c[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  int c2[] = {10, 100, 200};
  int c3[20];
  vector_t vs[2] = {{1, -2, 3}, {4, -5, -6}};
  cps_t cps;
  cpsc_t cpsc;
  cs_blob_t blob;
  cs_data_t data;
  cs_container_t container;
  cs_t *cs;
  int n;
  int ca[5] = {1, -2, 3, -4, 5};
  int tmp[10];
  doub_carr_t *dc;
  int *pi;
  pints_t api[5];
  numbers_struct_t *ns;
  refpint_t rpi[5];
  int i0 = 1, i1 = 2, *ptr_array[2] = {&i0, &i1}, array[2] = {3, 4};

  if (!old_windows_version)
  {
      const char str1[25] = "Hello";
      ok(cstr_length(str1, sizeof str1) == strlen(str1), "RPC cstr_length\n");
  }

  ok(sum_fixed_int_3d(m) == 4116, "RPC sum_fixed_int_3d\n");

  ok(sum_conf_array(c, 10) == 45, "RPC sum_conf_array\n");
  ok(sum_conf_array(&c[5], 2) == 11, "RPC sum_conf_array\n");
  ok(sum_conf_array(&c[7], 1) == 7, "RPC sum_conf_array\n");
  ok(sum_conf_array(&c[2], 0) == 0, "RPC sum_conf_array\n");

  ok(sum_conf_ptr_by_conf_ptr(1, c2, c) == 45, "RPC sum_conf_ptr_by_conf_ptr\n");
  ok(sum_conf_ptr_by_conf_ptr(3, c2, c) == 345, "RPC sum_conf_ptr_by_conf_ptr\n");
  c2[0] = 0;
  ok(sum_conf_ptr_by_conf_ptr(3, c2, c) == 300, "RPC sum_conf_ptr_by_conf_ptr\n");

  ok(sum_unique_conf_array(ca, 4) == -2, "RPC sum_unique_conf_array\n");
  ok(sum_unique_conf_ptr(ca, 5) == 3, "RPC sum_unique_conf_array\n");
  ok(sum_unique_conf_ptr(NULL, 10) == 0, "RPC sum_unique_conf_array\n");

  get_number_array(c3, &n);
  ok(n == 10, "RPC get_num_array\n");
  for (; n > 0; n--)
    ok(c3[n-1] == c[n-1], "get_num_array returned wrong value %d @ %d\n",
       c3[n-1], n);
  ok(sum_var_array(c, 10) == 45, "RPC sum_conf_array\n");
  ok(sum_var_array(&c[5], 2) == 11, "RPC sum_conf_array\n");
  ok(sum_var_array(&c[7], 1) == 7, "RPC sum_conf_array\n");
  ok(sum_var_array(&c[2], 0) == 0, "RPC sum_conf_array\n");

  ok(dot_two_vectors(vs) == -4, "RPC dot_two_vectors\n");
  cs = malloc(FIELD_OFFSET(cs_t, ca[5]));
  cs->n = 5;
  cs->ca[0] = 3;
  cs->ca[1] = 5;
  cs->ca[2] = -2;
  cs->ca[3] = -1;
  cs->ca[4] = -4;
  ok(sum_cs(cs) == 1, "RPC sum_cs\n");
  free(cs);

  n = 5;
  cps.pn = &n;
  cps.ca1 = &c[2];
  cps.n = 3;
  cps.ca2 = &c[3];
  ok(sum_cps(&cps) == 53, "RPC sum_cps\n");

  cpsc.a = 4;
  cpsc.b = 5;
  cpsc.c = 1;
  cpsc.ca = c;
  ok(sum_cpsc(&cpsc) == 6, "RPC sum_cpsc\n");
  cpsc.a = 4;
  cpsc.b = 5;
  cpsc.c = 0;
  cpsc.ca = c;
  ok(sum_cpsc(&cpsc) == 10, "RPC sum_cpsc\n");

  cpsc.ca = NULL;
  ok(get_cpsc(5, &cpsc) == 45, "RPC sum_cpsc\n");
  ok( cpsc.a == 10, "RPC get_cpsc %u\n", cpsc.a );
  for (n = 0; n < 10; n++) ok( cpsc.ca[n] == n, "RPC get_cpsc[%d] = %d\n", n, cpsc.ca[n] );

  memset( tmp, 0x33, sizeof(tmp) );
  cpsc.ca = tmp;
  ok(get_cpsc(4, &cpsc) == 28, "RPC sum_cpsc\n");
  ok( cpsc.a == 8, "RPC get_cpsc %u\n", cpsc.a );
  ok( cpsc.ca == tmp, "RPC get_cpsc %p/%p\n", cpsc.ca, tmp );
  for (n = 0; n < 8; n++) ok( cpsc.ca[n] == n, "RPC get_cpsc[%d] = %d\n", n, cpsc.ca[n] );

  ok(sum_toplev_conf_2n(c, 3) == 15, "RPC sum_toplev_conf_2n\n");
  ok(sum_toplev_conf_cond(c, 5, 6, 1) == 10, "RPC sum_toplev_conf_cond\n");
  ok(sum_toplev_conf_cond(c, 5, 6, 0) == 15, "RPC sum_toplev_conf_cond\n");

  dc = malloc(FIELD_OFFSET(doub_carr_t, a[2]));
  dc->n = 2;
  dc->a[0] = malloc(sizeof(doub_carr_1_t) + 3);
  dc->a[0]->n = 3;
  dc->a[0]->a[0] = 5;
  dc->a[0]->a[1] = 1;
  dc->a[0]->a[2] = 8;
  dc->a[1] = malloc(sizeof(doub_carr_1_t) + 2);
  dc->a[1]->n = 2;
  dc->a[1]->a[0] = 2;
  dc->a[1]->a[1] = 3;
  ok(sum_doub_carr(dc) == 19, "RPC sum_doub_carr\n");
  free(dc->a[0]);
  free(dc->a[1]);
  free(dc);

  dc = NULL;
  make_pyramid_doub_carr(4, &dc);
  ok(check_pyramid_doub_carr(dc), "RPC make_pyramid_doub_carr\n");
  free_pyramid_doub_carr(dc);

  ok(sum_L1_norms(2, vs) == 21, "RPC sum_L1_norms\n");

  memset(api, 0, sizeof(api));
  pi = malloc(sizeof(*pi));
  *pi = -1;
  api[0].pi = pi;
  get_numbers(1, 1, api);
  ok(api[0].pi == pi, "RPC conformant varying array [out] pointer changed from %p to %p\n", pi, api[0].pi);
  ok(*api[0].pi == 0, "pi unmarshalled incorrectly %d\n", *api[0].pi);

  if (!old_windows_version)
  {
      ns = calloc(1, FIELD_OFFSET(numbers_struct_t, numbers[5]));
      ns->length = 5;
      ns->size = 5;
      ns->numbers[0].pi = pi;
      get_numbers_struct(&ns);
      ok(ns->numbers[0].pi == pi, "RPC conformant varying struct embedded pointer changed from %p to %p\n", pi, ns->numbers[0].pi);
      ok(*ns->numbers[0].pi == 5, "pi unmarshalled incorrectly %d\n", *ns->numbers[0].pi);
      free(ns);
  }
  free(pi);

  pi = malloc(5 * sizeof(*pi));
  pi[0] = 3;  rpi[0] = &pi[0];
  pi[1] = 5;  rpi[1] = &pi[1];
  pi[2] = -2; rpi[2] = &pi[2];
  pi[3] = -1; rpi[3] = &pi[3];
  pi[4] = -4; rpi[4] = &pi[4];
  ok(sum_complex_array(5, rpi) == 1, "RPC sum_complex_array\n");
  free(pi);

  ok(sum_ptr_array(ptr_array) == 3, "RPC sum_ptr_array\n");
  ok(sum_array_ptr(&array) == 7, "RPC sum_array_ptr\n");

  blob.n = ARRAY_SIZE(c);
  blob.ca = c;
  n = sum_blob(&blob);
  ok(n == 45, "RPC sum_blob = %d\n", n);

  data.blob.n = ARRAY_SIZE(c);
  data.blob.ca = c;
  n = sum_data(&data);
  ok(n == 45, "RPC sum_data = %d\n", n);

  container.data.blob.n = ARRAY_SIZE(c);
  container.data.blob.ca = c;
  n = sum_container(&container);
  ok(n == 45, "RPC sum_container = %d\n", n);
}

void __cdecl s_authinfo_test(unsigned int protseq, int secure)
{
    RPC_BINDING_HANDLE binding;
    RPC_STATUS status;
    ULONG level, authnsvc;
    RPC_AUTHZ_HANDLE privs;
    unsigned char *principal;

    binding = I_RpcGetCurrentCallHandle();
    ok(binding != NULL, "I_RpcGetCurrentCallHandle returned NULL\n");

    level = authnsvc = 0xdeadbeef;
    privs = (RPC_AUTHZ_HANDLE)0xdeadbeef;
    principal = (unsigned char *)0xdeadbeef;

    if (secure || protseq == RPC_PROTSEQ_LRPC)
    {
        status = RpcBindingInqAuthClientA(binding, &privs, &principal, &level, &authnsvc, NULL);
        if (status == RPC_S_CANNOT_SUPPORT)
        {
            win_skip("RpcBindingInqAuthClientA not supported\n");
            return;
        }
        ok(status == RPC_S_OK, "expected RPC_S_OK got %lu\n", status);
        ok(privs != (RPC_AUTHZ_HANDLE)0xdeadbeef, "privs unchanged\n");
        ok(principal != (unsigned char *)0xdeadbeef, "principal unchanged\n");
        if (protseq != RPC_PROTSEQ_LRPC)
        {
            todo_wine
            ok(principal != NULL, "NULL principal\n");
        }
        if (protseq == RPC_PROTSEQ_LRPC && principal)
        {
            int len;
            char *spn;

            len = WideCharToMultiByte(CP_ACP, 0, (const WCHAR *)privs, -1, NULL, 0, NULL, NULL);
            spn = malloc(len);
            WideCharToMultiByte(CP_ACP, 0, (const WCHAR *)privs, -1, spn, len, NULL, NULL);

            ok(!strcmp(domain_and_user, spn), "expected %s got %s\n", domain_and_user, spn);
            free(spn);
        }
        ok(level == RPC_C_AUTHN_LEVEL_PKT_PRIVACY, "level unchanged\n");
        ok(authnsvc == RPC_C_AUTHN_WINNT, "authnsvc unchanged\n");
        RpcStringFreeA(&principal);

        status = RpcBindingInqAuthClientA(NULL, &privs, &principal, &level, &authnsvc, NULL);
        ok(status == RPC_S_OK, "expected RPC_S_OK got %lu\n", status);
        RpcStringFreeA(&principal);

        status = RpcBindingInqAuthClientExA(NULL, &privs, &principal, &level, &authnsvc, NULL, 0);
        ok(status == RPC_S_OK, "expected RPC_S_OK got %lu\n", status);
        RpcStringFreeA(&principal);

        status = RpcImpersonateClient(NULL);
        ok(status == RPC_S_OK, "expected RPC_S_OK got %lu\n", status);
        status = RpcRevertToSelf();
        ok(status == RPC_S_OK, "expected RPC_S_OK got %lu\n", status);

    }
    else
    {
        status = RpcBindingInqAuthClientA(binding, &privs, &principal, &level, &authnsvc, NULL);
        ok(status == RPC_S_BINDING_HAS_NO_AUTH, "expected RPC_S_BINDING_HAS_NO_AUTH got %lu\n", status);
        ok(privs == (RPC_AUTHZ_HANDLE)0xdeadbeef, "got %p\n", privs);
        ok(principal == (unsigned char *)0xdeadbeef, "got %s\n", principal);
        ok(level == 0xdeadbeef, "got %lu\n", level);
        ok(authnsvc == 0xdeadbeef, "got %lu\n", authnsvc);
    }
}

static void test_handle_return(void)
{
    ctx_handle_t handle, handle2;

    handle = get_handle();
    test_handle(handle);
    get_handle_by_ptr(&handle2);
    test_handle(handle2);
}

static void
run_tests(void)
{
  basic_tests();
  union_tests();
  pointer_tests();
  array_tests();
  context_handle_test();
  test_handle_return();
}

static void
set_auth_info(RPC_BINDING_HANDLE handle)
{
    RPC_STATUS status;
    RPC_SECURITY_QOS qos;

    qos.Version = 1;
    qos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
    qos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
    qos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;

    status = pRpcBindingSetAuthInfoExA(handle, (RPC_CSTR)domain_and_user, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
                                       RPC_C_AUTHN_WINNT, NULL, 0, &qos);
    ok(status == RPC_S_OK, "RpcBindingSetAuthInfoExA failed %ld\n", status);
}

#define test_is_server_listening(a,b) _test_is_server_listening(__LINE__,a,b)
static void _test_is_server_listening(unsigned line, RPC_BINDING_HANDLE binding, RPC_STATUS expected_status)
{
    RPC_STATUS status;
    status = RpcMgmtIsServerListening(binding);
    ok_(__FILE__,line)(status == expected_status, "RpcMgmtIsServerListening returned %lu, expected %lu\n",
                       status, expected_status);
}

#define test_is_server_listening2(a,b,c) _test_is_server_listening2(__LINE__,a,b,c)
static void _test_is_server_listening2(unsigned line, RPC_BINDING_HANDLE binding, RPC_STATUS expected_status,
        RPC_STATUS expected_status2)
{
    RPC_STATUS status;
    status = RpcMgmtIsServerListening(binding);
    ok_(__FILE__,line)(status == expected_status || status == expected_status2,
                       "RpcMgmtIsServerListening returned %lu, expected %lu or %lu\n",
                       status, expected_status, expected_status2);
}

static void
client(const char *test)
{
  static unsigned char iptcp[] = "ncacn_ip_tcp";
  static unsigned char np[] = "ncacn_np";
  static unsigned char ncalrpc[] = "ncalrpc";
  static unsigned char address[] = "127.0.0.1";
  static unsigned char address_np[] = "\\\\.";
  static unsigned char port[] = PORT;
  static unsigned char pipe[] = PIPE;
  static unsigned char guid[] = "00000000-4114-0704-2301-000000000000";
  static unsigned char explicit_handle_guid[] = "00000000-4114-0704-2301-000000000002";

  unsigned char *binding;

  if (strcmp(test, "tcp_basic") == 0)
  {
    ok(RPC_S_OK == RpcStringBindingComposeA(NULL, iptcp, address, port, NULL, &binding), "RpcStringBindingCompose\n");
    ok(RPC_S_OK == RpcBindingFromStringBindingA(binding, &IMixedServer_IfHandle), "RpcBindingFromStringBinding\n");

    run_tests();
    authinfo_test(RPC_PROTSEQ_TCP, 0);
    test_I_RpcBindingInqLocalClientPID(RPC_PROTSEQ_TCP, IMixedServer_IfHandle);
    test_is_server_listening2(IMixedServer_IfHandle, RPC_S_OK, RPC_S_ACCESS_DENIED);

    ok(RPC_S_OK == RpcStringFreeA(&binding), "RpcStringFree\n");
    ok(RPC_S_OK == RpcBindingFree(&IMixedServer_IfHandle), "RpcBindingFree\n");
  }
  else if (strcmp(test, "tcp_secure") == 0)
  {
    ok(RPC_S_OK == RpcStringBindingComposeA(NULL, iptcp, address, port, NULL, &binding), "RpcStringBindingCompose\n");
    ok(RPC_S_OK == RpcBindingFromStringBindingA(binding, &IMixedServer_IfHandle), "RpcBindingFromStringBinding\n");

    set_auth_info(IMixedServer_IfHandle);
    authinfo_test(RPC_PROTSEQ_TCP, 1);
    test_I_RpcBindingInqLocalClientPID(RPC_PROTSEQ_TCP, IMixedServer_IfHandle);
    test_is_server_listening(IMixedServer_IfHandle, RPC_S_ACCESS_DENIED);

    ok(RPC_S_OK == RpcStringFreeA(&binding), "RpcStringFree\n");
    ok(RPC_S_OK == RpcBindingFree(&IMixedServer_IfHandle), "RpcBindingFree\n");
  }
  else if (strcmp(test, "ncalrpc_basic") == 0)
  {
    ok(RPC_S_OK == RpcStringBindingComposeA(NULL, ncalrpc, NULL, guid, NULL, &binding), "RpcStringBindingCompose\n");
    ok(RPC_S_OK == RpcBindingFromStringBindingA(binding, &IMixedServer_IfHandle), "RpcBindingFromStringBinding\n");

    run_tests(); /* can cause RPC_X_BAD_STUB_DATA exception */
    authinfo_test(RPC_PROTSEQ_LRPC, 0);
    test_I_RpcBindingInqLocalClientPID(RPC_PROTSEQ_LRPC, IMixedServer_IfHandle);
    test_is_server_listening(IMixedServer_IfHandle, RPC_S_OK);

    ok(RPC_S_OK == RpcStringFreeA(&binding), "RpcStringFree\n");
    ok(RPC_S_OK == RpcBindingFree(&IMixedServer_IfHandle), "RpcBindingFree\n");
  }
  else if (strcmp(test, "ncalrpc_autolisten") == 0)
  {
    ok(RPC_S_OK == RpcStringBindingComposeA(NULL, ncalrpc, NULL, guid, NULL, &binding), "RpcStringBindingCompose\n");
    ok(RPC_S_OK == RpcBindingFromStringBindingA(binding, &IMixedServer_IfHandle), "RpcBindingFromStringBinding\n");

    run_tests();
    authinfo_test(RPC_PROTSEQ_LRPC, 0);
    test_I_RpcBindingInqLocalClientPID(RPC_PROTSEQ_LRPC, IMixedServer_IfHandle);
    todo_wine
    test_is_server_listening(IMixedServer_IfHandle, RPC_S_NOT_LISTENING);

    stop_autolisten();
    ok(int_return() == INT_CODE, "RPC int_return\n");

    ok(RPC_S_OK == RpcStringFreeA(&binding), "RpcStringFree\n");
    ok(RPC_S_OK == RpcBindingFree(&IMixedServer_IfHandle), "RpcBindingFree\n");
  }
  else if (strcmp(test, "ncalrpc_secure") == 0)
  {
    ok(RPC_S_OK == RpcStringBindingComposeA(NULL, ncalrpc, NULL, guid, NULL, &binding), "RpcStringBindingCompose\n");
    ok(RPC_S_OK == RpcBindingFromStringBindingA(binding, &IMixedServer_IfHandle), "RpcBindingFromStringBinding\n");

    set_auth_info(IMixedServer_IfHandle);
    authinfo_test(RPC_PROTSEQ_LRPC, 1);
    test_I_RpcBindingInqLocalClientPID(RPC_PROTSEQ_LRPC, IMixedServer_IfHandle);
    test_is_server_listening(IMixedServer_IfHandle, RPC_S_OK);

    ok(RPC_S_OK == RpcStringFreeA(&binding), "RpcStringFree\n");
    ok(RPC_S_OK == RpcBindingFree(&IMixedServer_IfHandle), "RpcBindingFree\n");
  }
  else if (strcmp(test, "np_basic") == 0)
  {
    ok(RPC_S_OK == RpcStringBindingComposeA(NULL, np, address_np, pipe, NULL, &binding), "RpcStringBindingCompose\n");
    ok(RPC_S_OK == RpcBindingFromStringBindingA(binding, &IMixedServer_IfHandle), "RpcBindingFromStringBinding\n");

    test_is_server_listening(IMixedServer_IfHandle, RPC_S_OK);
    run_tests();
    authinfo_test(RPC_PROTSEQ_NMP, 0);
    test_I_RpcBindingInqLocalClientPID(RPC_PROTSEQ_NMP, IMixedServer_IfHandle);
    test_is_server_listening(IMixedServer_IfHandle, RPC_S_OK);
    stop();
    test_is_server_listening(IMixedServer_IfHandle, RPC_S_NOT_LISTENING);

    ok(RPC_S_OK == RpcStringFreeA(&binding), "RpcStringFree\n");
    ok(RPC_S_OK == RpcBindingFree(&IMixedServer_IfHandle), "RpcBindingFree\n");
  }
  else if (strcmp(test, "np_basic_interp") == 0)
  {
    set_interp_interface();

    ok(RPC_S_OK == RpcStringBindingComposeA(NULL, np, address_np, pipe, NULL, &binding), "RpcStringBindingCompose\n");
    ok(RPC_S_OK == RpcBindingFromStringBindingA(binding, &IInterpServer_IfHandle), "RpcBindingFromStringBinding\n");

    test_is_server_listening(IInterpServer_IfHandle, RPC_S_OK);
    run_tests();
    authinfo_test(RPC_PROTSEQ_NMP, 0);
    test_I_RpcBindingInqLocalClientPID(RPC_PROTSEQ_NMP, IInterpServer_IfHandle);
    test_is_server_listening(IInterpServer_IfHandle, RPC_S_OK);

    ok(RPC_S_OK == RpcStringFreeA(&binding), "RpcStringFree\n");
    ok(RPC_S_OK == RpcBindingFree(&IInterpServer_IfHandle), "RpcBindingFree\n");
  }
  else if (strcmp(test, "explicit_handle") == 0)
  {
    IMixedServer_IfHandle = NULL;
    ok(RPC_S_OK == RpcStringBindingComposeA(NULL, ncalrpc, NULL, explicit_handle_guid, NULL, &binding), "RpcStringBindingCompose\n");
    ok(RPC_S_OK == RpcBindingFromStringBindingA(binding, &IMixedServer_IfHandle), "RpcBindingFromStringBinding\n");

    test_is_server_listening(IMixedServer_IfHandle, RPC_S_OK);

    ok(add(IMixedServer_IfHandle, 2, 3) == 5, "RPC add\n");
    ok(getNum(7, IMixedServer_IfHandle) == 9, "RPC getNum\n");
    Shutdown(IMixedServer_IfHandle);

    ok(RPC_S_OK == RpcStringFreeA(&binding), "RpcStringFree\n");
    ok(RPC_S_OK == RpcBindingFree(&IMixedServer_IfHandle), "RpcBindingFree\n");
  }
}

static void
server(void)
{
  static unsigned char iptcp[] = "ncacn_ip_tcp";
  static unsigned char port[] = PORT;
  static unsigned char np[] = "ncacn_np";
  static unsigned char pipe[] = PIPE;
  static unsigned char ncalrpc[] = "ncalrpc";
  static unsigned char guid[] = "00000000-4114-0704-2301-000000000000";
  static unsigned char explicit_handle_guid[] = "00000000-4114-0704-2301-000000000002";
  RPC_STATUS status, iptcp_status, np_status, ncalrpc_status;
  DWORD ret;

  /* needed for tests involving interface pointers */
  CoInitializeEx(NULL, COINIT_MULTITHREADED);

  iptcp_status = RpcServerUseProtseqEpA(iptcp, 20, port, NULL);
  ok(iptcp_status == RPC_S_OK, "RpcServerUseProtseqEp(ncacn_ip_tcp) failed with status %ld\n", iptcp_status);

  ncalrpc_status = RpcServerUseProtseqEpA(ncalrpc, 0, guid, NULL);
  ok(ncalrpc_status == RPC_S_OK, "RpcServerUseProtseqEp(ncalrpc) failed with status %ld\n", ncalrpc_status);

  np_status = RpcServerUseProtseqEpA(np, 0, pipe, NULL);
  if (np_status == RPC_S_PROTSEQ_NOT_SUPPORTED)
    skip("Protocol sequence ncacn_np is not supported\n");
  else
    ok(np_status == RPC_S_OK, "RpcServerUseProtseqEp(ncacn_np) failed with status %ld\n", np_status);

  if (pRpcServerRegisterIfEx)
  {
    trace("Using RpcServerRegisterIfEx\n");
    status = pRpcServerRegisterIfEx(s_IMixedServer_v0_0_s_ifspec, NULL, NULL,
                                    RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH,
                                    RPC_C_LISTEN_MAX_CALLS_DEFAULT, NULL);
    ok(status == RPC_S_OK, "RpcServerRegisterIfEx failed with status %ld\n", status);
    status = pRpcServerRegisterIfEx(s_IInterpServer_v0_0_s_ifspec, NULL, NULL,
                                    RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH,
                                    RPC_C_LISTEN_MAX_CALLS_DEFAULT, NULL);
    ok(status == RPC_S_OK, "RpcServerRegisterIfEx failed with status %ld\n", status);
  }
  else
  {
    status = RpcServerRegisterIf(s_IMixedServer_v0_0_s_ifspec, NULL, NULL);
    ok(status == RPC_S_OK, "RpcServerRegisterIf failed with status %ld\n", status);
    status = RpcServerRegisterIf(s_IInterpServer_v0_0_s_ifspec, NULL, NULL);
    ok(status == RPC_S_OK, "RpcServerRegisterIf failed with status %ld\n", status);
  }
  test_is_server_listening(NULL, RPC_S_NOT_LISTENING);
  status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
  ok(status == RPC_S_OK, "RpcServerListen failed with status %ld\n", status);
  test_is_server_listening(NULL, RPC_S_OK);
  stop_event = CreateEventW(NULL, FALSE, FALSE, NULL);
  ok(stop_event != NULL, "CreateEvent failed with error %ld\n", GetLastError());

  if (iptcp_status == RPC_S_OK)
    run_client("tcp_basic");
  else
    skip("tcp tests skipped due to earlier failure\n");

  if (ncalrpc_status == RPC_S_OK)
  {
    run_client("ncalrpc_basic");

    /* we don't need to register RPC_C_AUTHN_WINNT for ncalrpc */
    run_client("ncalrpc_secure");
  }
  else
    skip("lrpc tests skipped due to earlier failure\n");

  if (np_status == RPC_S_OK)
  {
    run_client("np_basic_interp");
    run_client("np_basic");
  }
  else
  {
    skip("np_basic tests skipped due to earlier failure\n");
    /* np client is what signals stop_event, so bail out if we didn't run do it */
    return;
  }

  ret = WaitForSingleObject(stop_event, 1000);
  ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject\n");

  /* if the stop event didn't fire then RpcMgmtWaitServerListen will wait
   * forever, so don't bother calling it in this case */
  if (ret == WAIT_OBJECT_0)
  {
    status = RpcMgmtWaitServerListen();
    ok(status == RPC_S_OK, "RpcMgmtWaitServerListening failed with status %ld\n", status);
  }

  CloseHandle(stop_event);
  stop_event = NULL;

  if (pRpcServerRegisterIfEx)
  {
    status = pRpcServerRegisterIfEx(s_IMixedServer_v0_0_s_ifspec, NULL, NULL,
        RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH | RPC_IF_AUTOLISTEN,
        RPC_C_LISTEN_MAX_CALLS_DEFAULT, NULL);
    ok(status == RPC_S_OK, "RpcServerRegisterIf() failed: %lu\n", status);

    run_client("ncalrpc_autolisten");

    status = RpcServerUnregisterIf(s_IMixedServer_v0_0_s_ifspec, NULL, TRUE);
    ok(status == RPC_S_OK, "RpcServerUnregisterIf() failed: %lu\n", status);
  }

  /* explicit handle */
  stop_event = CreateEventW(NULL, FALSE, FALSE, NULL);
  ok(stop_event != NULL, "CreateEvent failed with error %ld\n", GetLastError());

  ncalrpc_status = RpcServerUseProtseqEpA(ncalrpc, 0, explicit_handle_guid, NULL);
  if (ncalrpc_status == RPC_S_PROTSEQ_NOT_SUPPORTED)
    skip("Protocol sequence ncacn_np is not supported\n");
  else
    ok(ncalrpc_status == RPC_S_OK, "RpcServerUseProtseqEp(ncacn_np) failed with status %ld\n", ncalrpc_status);

  if (pRpcServerRegisterIfEx)
  {
    trace("Using RpcServerRegisterIfEx\n");
    status = pRpcServerRegisterIfEx(s_RPCExplicitHandle_v0_0_s_ifspec, NULL, NULL,
                                    RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH,
                                    RPC_C_LISTEN_MAX_CALLS_DEFAULT, NULL);
    ok(status == RPC_S_OK, "RpcServerRegisterIfEx failed with status %ld\n", status);
    test_is_server_listening(NULL, RPC_S_NOT_LISTENING);
    status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
    ok(status == RPC_S_OK, "RpcServerListen failed with status %ld\n", status);
  }
  else
  {
    status = RpcServerRegisterIf(s_RPCExplicitHandle_v0_0_s_ifspec, NULL, NULL);
    ok(status == RPC_S_OK, "RpcServerRegisterIf failed with status %ld\n", status);
    status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
    ok(status == RPC_S_OK, "RpcServerListen failed with status %ld\n", status);
  }

  test_is_server_listening(NULL, RPC_S_OK);

  run_client("explicit_handle");

  ret = WaitForSingleObject(stop_event, 1000);
  ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject\n");

  if (pRpcServerRegisterIfEx)
  {
    status = RpcServerUnregisterIf(s_RPCExplicitHandle_v0_0_s_ifspec, NULL, TRUE);
    ok(status == RPC_S_OK, "RpcServerUnregisterIf() failed: %lu\n", status);
  }

  CloseHandle(stop_event);
  stop_event = NULL;

  CoUninitialize();
}

static DWORD WINAPI listen_test_client_thread(void *binding)
{
    RPC_STATUS status;

    status = RpcBindingFromStringBindingA(binding, &IMixedServer_IfHandle);
    ok(status == RPC_S_OK, "RpcBindingFromStringBinding\n");

    test_is_server_listening(IMixedServer_IfHandle, RPC_S_OK);
    stop();
    trace("stopped\n");

    status = RpcBindingFree(&IMixedServer_IfHandle);
    ok(status == RPC_S_OK, "RpcBindingFree\n");
    return 0;
}

static DWORD WINAPI wait_listen_proc(void *arg)
{
    RPC_STATUS status;

    trace("waiting\n");
    status = RpcMgmtWaitServerListen();
    ok(status == RPC_S_OK, "RpcMgmtWaitServerListening failed with status %ld\n", status);
    trace("done\n");

    return 0;
}

static void test_stop_wait_for_call(unsigned char *binding)
{
    HANDLE client_thread, wait_listen_thread;
    RPC_STATUS status;
    DWORD ret;

    status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
    ok(status == RPC_S_OK, "RpcServerListen failed with status %ld\n", status);
    test_is_server_listening(NULL, RPC_S_OK);

    stop_wait_event = CreateEventW(NULL, FALSE, FALSE, NULL);
    ok(stop_wait_event != NULL, "CreateEvent failed with error %ld\n", GetLastError());
    stop_event = CreateEventW(NULL, FALSE, FALSE, NULL);
    ok(stop_event != NULL, "CreateEvent failed with error %ld\n", GetLastError());

    wait_listen_thread = CreateThread(NULL, 0, wait_listen_proc, 0, 0, NULL);
    ok(wait_listen_thread != NULL, "CreateThread failed\n");

    client_thread = CreateThread(NULL, 0, listen_test_client_thread, binding, 0, NULL);
    ok(client_thread != NULL, "CreateThread failed\n");
    CloseHandle(client_thread);

    ret = WaitForSingleObject(stop_event, 10000);
    ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject\n");

    status = RpcMgmtStopServerListening(NULL);
    ok(status == RPC_S_OK, "RpcMgmtStopServerListening\n");
    test_is_server_listening(NULL, RPC_S_NOT_LISTENING);

    ret = WaitForSingleObject(wait_listen_thread, 500);
    ok(WAIT_TIMEOUT == ret, "WaitForSingleObject\n");

    SetEvent(stop_wait_event);

    ret = WaitForSingleObject(wait_listen_thread, 10000);
    ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject returned %lu\n", ret);

    CloseHandle(wait_listen_thread);

    CloseHandle(stop_wait_event);
    stop_wait_event = NULL;
    CloseHandle(stop_event);
    stop_event = NULL;
}

static void test_server_listening(void)
{
    static unsigned char np[] = "ncacn_np";
    static unsigned char address_np[] = "\\\\.";
    static unsigned char pipe[] = PIPE "listen_test";
    static unsigned char ncalrpc[] = "ncalrpc";
    static unsigned char guid[] = "00000000-4114-0704-2302-000000000000";
    unsigned char *binding;
    RPC_STATUS status;

    status = RpcServerUseProtseqEpA(np, 0, pipe, NULL);
    ok(status == RPC_S_OK, "RpcServerUseProtseqEp(ncacn_np) failed with status %ld\n", status);

    status = RpcServerRegisterIf(s_IMixedServer_v0_0_s_ifspec, NULL, NULL);
    ok(status == RPC_S_OK, "RpcServerRegisterIf failed with status %ld\n", status);

    test_is_server_listening(NULL, RPC_S_NOT_LISTENING);
    status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
    ok(status == RPC_S_OK, "RpcServerListen failed with status %ld\n", status);
    test_is_server_listening(NULL, RPC_S_OK);

    status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
    ok(status == RPC_S_ALREADY_LISTENING, "RpcServerListen failed with status %ld\n", status);

    status = RpcMgmtStopServerListening(NULL);
    ok(status == RPC_S_OK, "RpcMgmtStopServerListening\n");
    test_is_server_listening(NULL, RPC_S_NOT_LISTENING);

    status = RpcMgmtWaitServerListen();
    ok(status == RPC_S_OK, "RpcMgmtWaitServerListening failed with status %ld\n", status);

    status = RpcMgmtWaitServerListen();
    ok(status == RPC_S_NOT_LISTENING, "RpcMgmtWaitServerListening failed with status %ld\n", status);

    /* test that server stop waits for a call in progress */
    status = RpcStringBindingComposeA(NULL, np, address_np, pipe, NULL, &binding);
    ok(status == RPC_S_OK, "RpcStringBindingCompose\n");

    test_stop_wait_for_call(binding);

    status = RpcStringFreeA(&binding);
    ok(status == RPC_S_OK, "RpcStringFree\n");

    /* repeat the test using ncalrpc */
    status = RpcServerUseProtseqEpA(ncalrpc, 0, guid, NULL);
    ok(status == RPC_S_OK, "RpcServerUseProtseqEp(ncalrpc) failed with status %ld\n", status);

    status = RpcStringBindingComposeA(NULL, ncalrpc, NULL, guid, NULL, &binding);
    ok(status == RPC_S_OK, "RpcStringBindingCompose\n");

    test_stop_wait_for_call(binding);

    status = RpcStringFreeA(&binding);
    ok(status == RPC_S_OK, "RpcStringFree\n");
}

static void create_server_process(PROCESS_INFORMATION *info)
{
    SECURITY_ATTRIBUTES sec_attr = { sizeof(sec_attr), NULL, TRUE };
    HANDLE ready_event;
    char cmdline[MAX_PATH];
    STARTUPINFOA startup;
    DWORD ret;

    memset(&startup, 0, sizeof startup);
    startup.cb = sizeof startup;

    ready_event = CreateEventW(&sec_attr, TRUE, FALSE, NULL);
    ok(ready_event != NULL, "CreateEvent failed: %lu\n", GetLastError());

    sprintf(cmdline, "%s server run %Ix", progname, (UINT_PTR)ready_event);
    trace("running server process...\n");
    ok(CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, 0L, NULL, NULL, &startup, info), "CreateProcess\n");
    ret = WaitForSingleObject(ready_event, 10000);
    ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject\n");

    ok(CloseHandle(ready_event), "CloseHandle\n");
}

static void run_server(HANDLE ready_event)
{
    static unsigned char np[] = "ncacn_np";
    static unsigned char pipe[] = PIPE "term_test";
    RPC_STATUS status;
    BOOL ret;

    status = RpcServerUseProtseqEpA(np, 0, pipe, NULL);
    ok(status == RPC_S_OK, "RpcServerUseProtseqEp(ncacn_np) failed with status %ld\n", status);

    status = RpcServerRegisterIf(s_IMixedServer_v0_0_s_ifspec, NULL, NULL);
    ok(status == RPC_S_OK, "RpcServerRegisterIf failed with status %ld\n", status);

    test_is_server_listening(NULL, RPC_S_NOT_LISTENING);
    status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
    ok(status == RPC_S_OK, "RpcServerListen failed with status %ld\n", status);

    stop_event = CreateEventW(NULL, FALSE, FALSE, NULL);
    ok(stop_event != NULL, "CreateEvent failed with error %ld\n", GetLastError());

    ret = SetEvent(ready_event);
    ok(ret, "SetEvent failed: %lu\n", GetLastError());

    ret = WaitForSingleObject(stop_event, 5000);
    ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject\n");

    status = RpcMgmtWaitServerListen();
    ok(status == RPC_S_OK, "RpcMgmtWaitServerListening failed with status %ld\n", status);

    CloseHandle(stop_event);
    stop_event = NULL;
}

static DWORD WINAPI basic_tests_thread(void *arg)
{
    basic_tests();
    return 0;
}

static void test_reconnect(void)
{
    static unsigned char np[] = "ncacn_np";
    static unsigned char address_np[] = "\\\\.";
    static unsigned char pipe[] = PIPE "term_test";
    PROCESS_INFORMATION info;
    unsigned char *binding;
    HANDLE threads[32];
    unsigned i;
    DWORD ret;

    create_server_process(&info);

    ok(RPC_S_OK == RpcStringBindingComposeA(NULL, np, address_np, pipe, NULL, &binding), "RpcStringBindingCompose\n");
    ok(RPC_S_OK == RpcBindingFromStringBindingA(binding, &IMixedServer_IfHandle), "RpcBindingFromStringBinding\n");

    for (i = 0; i < ARRAY_SIZE(threads); i++)
    {
        threads[i] = CreateThread(NULL, 0, basic_tests_thread, 0, 0, NULL);
        ok(threads[i] != NULL, "CreateThread failed: %lu\n", GetLastError());
    }

    for (i = 0; i < ARRAY_SIZE(threads); i++)
    {
        ret = WaitForSingleObject(threads[i], 10000);
        ok(WAIT_OBJECT_0 == ret, "WaitForSingleObject\n");
        CloseHandle(threads[i]);
    }

    stop();

    wait_child_process(&info);

    /* create new server, rpcrt4 will connect to it once sending to existing connection fails
     * that current connection is broken. */
    create_server_process(&info);
    basic_tests();
    stop();

    wait_child_process(&info);

    ok(RPC_S_OK == RpcStringFreeA(&binding), "RpcStringFree\n");
    ok(RPC_S_OK == RpcBindingFree(&IMixedServer_IfHandle), "RpcBindingFree\n");
}

static BOOL is_process_elevated(void)
{
    HANDLE token;
    if (OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &token ))
    {
        TOKEN_ELEVATION_TYPE type;
        DWORD size;
        BOOL ret;

        ret = GetTokenInformation( token, TokenElevationType, &type, sizeof(type), &size );
        CloseHandle( token );
        return (ret && type == TokenElevationTypeFull);
    }
    return FALSE;
}

static BOOL is_firewall_enabled(void)
{
    HRESULT hr, init;
    INetFwMgr *mgr = NULL;
    INetFwPolicy *policy = NULL;
    INetFwProfile *profile = NULL;
    VARIANT_BOOL enabled = VARIANT_FALSE;

    init = CoInitializeEx( 0, COINIT_APARTMENTTHREADED );

    hr = CoCreateInstance( &CLSID_NetFwMgr, NULL, CLSCTX_INPROC_SERVER, &IID_INetFwMgr,
                           (void **)&mgr );
    ok( hr == S_OK, "got %08lx\n", hr );
    if (hr != S_OK) goto done;

    hr = INetFwMgr_get_LocalPolicy( mgr, &policy );
    ok( hr == S_OK, "got %08lx\n", hr );
    if (hr != S_OK) goto done;

    hr = INetFwPolicy_get_CurrentProfile( policy, &profile );
    if (hr != S_OK) goto done;

    hr = INetFwProfile_get_FirewallEnabled( profile, &enabled );
    ok( hr == S_OK, "got %08lx\n", hr );

done:
    if (policy) INetFwPolicy_Release( policy );
    if (profile) INetFwProfile_Release( profile );
    if (mgr) INetFwMgr_Release( mgr );
    if (SUCCEEDED( init )) CoUninitialize();
    return (enabled == VARIANT_TRUE);
}

enum firewall_op
{
    APP_ADD,
    APP_REMOVE
};

static HRESULT set_firewall( enum firewall_op op )
{
    HRESULT hr, init;
    INetFwMgr *mgr = NULL;
    INetFwPolicy *policy = NULL;
    INetFwProfile *profile = NULL;
    INetFwAuthorizedApplication *app = NULL;
    INetFwAuthorizedApplications *apps = NULL;
    BSTR name, image = SysAllocStringLen( NULL, MAX_PATH );

    if (!GetModuleFileNameW( NULL, image, MAX_PATH ))
    {
        SysFreeString( image );
        return E_FAIL;
    }
    init = CoInitializeEx( 0, COINIT_APARTMENTTHREADED );

    hr = CoCreateInstance( &CLSID_NetFwMgr, NULL, CLSCTX_INPROC_SERVER, &IID_INetFwMgr,
                           (void **)&mgr );
    ok( hr == S_OK, "got %08lx\n", hr );
    if (hr != S_OK) goto done;

    hr = INetFwMgr_get_LocalPolicy( mgr, &policy );
    ok( hr == S_OK, "got %08lx\n", hr );
    if (hr != S_OK) goto done;

    hr = INetFwPolicy_get_CurrentProfile( policy, &profile );
    if (hr != S_OK) goto done;

    hr = INetFwProfile_get_AuthorizedApplications( profile, &apps );
    ok( hr == S_OK, "got %08lx\n", hr );
    if (hr != S_OK) goto done;

    hr = CoCreateInstance( &CLSID_NetFwAuthorizedApplication, NULL, CLSCTX_INPROC_SERVER,
                           &IID_INetFwAuthorizedApplication, (void **)&app );
    ok( hr == S_OK, "got %08lx\n", hr );
    if (hr != S_OK) goto done;

    hr = INetFwAuthorizedApplication_put_ProcessImageFileName( app, image );
    if (hr != S_OK) goto done;

    name = SysAllocString( L"rpcrt4_test" );
    hr = INetFwAuthorizedApplication_put_Name( app, name );
    SysFreeString( name );
    ok( hr == S_OK, "got %08lx\n", hr );
    if (hr != S_OK) goto done;

    if (op == APP_ADD)
        hr = INetFwAuthorizedApplications_Add( apps, app );
    else if (op == APP_REMOVE)
        hr = INetFwAuthorizedApplications_Remove( apps, image );
    else
        hr = E_INVALIDARG;

done:
    if (app) INetFwAuthorizedApplication_Release( app );
    if (apps) INetFwAuthorizedApplications_Release( apps );
    if (policy) INetFwPolicy_Release( policy );
    if (profile) INetFwProfile_Release( profile );
    if (mgr) INetFwMgr_Release( mgr );
    if (SUCCEEDED( init )) CoUninitialize();
    SysFreeString( image );
    return hr;
}

START_TEST(server)
{
  ULONG size = 0;
  int argc;
  char **argv;
  BOOL firewall_enabled = is_firewall_enabled(), firewall_disabled = FALSE;

  InitFunctionPointers();
  set_mixed_interface();

  ok(!GetUserNameExA(NameSamCompatible, NULL, &size), "GetUserNameExA\n");
  domain_and_user = malloc(size);
  ok(GetUserNameExA(NameSamCompatible, domain_and_user, &size), "GetUserNameExA\n");

  argc = winetest_get_mainargs(&argv);
  progname = argv[0];

  if (argc == 3)
  {
    RpcTryExcept
    {
      client(argv[2]);
    }
    RpcExcept(TRUE)
    {
      trace("Exception %ld\n", RpcExceptionCode());
    }
    RpcEndExcept
  }
  else if (argc == 4)
  {
    if (!strcmp(argv[3], "listen"))
    {
      test_server_listening();
    }
    else if(!strcmp(argv[2], "run"))
    {
      ULONG event;
      sscanf(argv[3], "%lx", &event);
      run_server(ULongToHandle(event));
    }
  }
  else
  {
    if (firewall_enabled)
    {
      if (is_process_elevated())
      {
        HRESULT hr = set_firewall(APP_ADD);
        if (hr == S_OK)
        {
          firewall_enabled = FALSE;
          firewall_disabled = TRUE;
        }
        else
        {
          skip("can't authorize app in firewall %08lx\n", hr);
        }
      }
      else
      {
          trace("no privileges, skipping tests to avoid firewall dialog\n");
      }
    }

    if (!firewall_enabled) server();

    /* Those tests cause occasional crashes on winxp and win2k3 */
    if (GetProcAddress(GetModuleHandleA("rpcrt4.dll"), "RpcExceptionFilter"))
        test_reconnect();
    else
        win_skip("Skipping reconnect tests on too old Windows version\n");

    run_client("test listen");
    if (firewall_disabled) set_firewall(APP_REMOVE);
  }

  free(domain_and_user);
}
